diff options
Diffstat (limited to 'chromium/content/browser/renderer_host')
208 files changed, 9967 insertions, 4602 deletions
diff --git a/chromium/content/browser/renderer_host/DEPS b/chromium/content/browser/renderer_host/DEPS index 34decdb0223..0848d364726 100644 --- a/chromium/content/browser/renderer_host/DEPS +++ b/chromium/content/browser/renderer_host/DEPS @@ -5,10 +5,6 @@ include_rules = [ "+third_party/zlib", "+third_party/libyuv", - # For single-process mode. - "+content/renderer/render_process_impl.h", - "+content/renderer/render_thread_impl.h", - # The renderer_host files should only call upwards in the layering via the # delegate interfaces. "-content/browser/web_contents", diff --git a/chromium/content/browser/renderer_host/backing_store_gtk.cc b/chromium/content/browser/renderer_host/backing_store_gtk.cc index 62365bed873..2eed33ace2f 100644 --- a/chromium/content/browser/renderer_host/backing_store_gtk.cc +++ b/chromium/content/browser/renderer_host/backing_store_gtk.cc @@ -29,10 +29,10 @@ #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/gtk/gtk_signal.h" -#include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util_internal.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" +#include "ui/gfx/x/x11_types.h" #include "ui/surface/transport_dib.h" namespace content { @@ -59,7 +59,7 @@ static const int kMaxVideoLayerSize = 23170; // Destroys the image and the associated shared memory structures. This is a // helper function for code using shared memory. -void DestroySharedImage(Display* display, +void DestroySharedImage(XDisplay* display, XImage* image, XShmSegmentInfo* shminfo) { XShmDetach(display, shminfo); @@ -72,7 +72,7 @@ void DestroySharedImage(Display* display, // XSyncExtension to push a callback into the X11 event queue and get a // callback instead of blocking until the event queue is cleared. // -// TODO(erg): If ui::GetXDisplay() ever gets fixed to handle multiple Displays, +// TODO(erg): If gfx::GetXDisplay() ever gets fixed to handle multiple Displays, // this must be modified to be per Display instead of a Singleton. class XSyncHandler { public: @@ -85,7 +85,7 @@ class XSyncHandler { } void PushPaintCounter(TransportDIB* dib, - Display* display, + XDisplay* display, Picture picture, Pixmap pixmap, const base::Closure& completion_callback); @@ -96,7 +96,7 @@ class XSyncHandler { // A struct that has cleanup and callback tasks that were queued into the // future and are run on |g_backing_store_sync_alarm| firing. struct BackingStoreEvents { - BackingStoreEvents(TransportDIB* dib, Display* d, Picture pic, Pixmap pix, + BackingStoreEvents(TransportDIB* dib, XDisplay* d, Picture pic, Pixmap pix, const base::Closure& c) : dib(dib), display(d), @@ -109,7 +109,7 @@ class XSyncHandler { TransportDIB* dib; // The display we're running on. - Display* display; + XDisplay* display; // Data to delete. Picture picture; @@ -142,7 +142,7 @@ class XSyncHandler { }; void XSyncHandler::PushPaintCounter(TransportDIB* dib, - Display* display, + XDisplay* display, Picture picture, Pixmap pixmap, const base::Closure& completion_callback) { @@ -153,7 +153,7 @@ void XSyncHandler::PushPaintCounter(TransportDIB* dib, // alarm when it is processed. XSyncValue value; XSyncIntToValue(&value, 1); - XSyncChangeCounter(ui::GetXDisplay(), + XSyncChangeCounter(gfx::GetXDisplay(), backing_store_sync_counter_, value); } @@ -164,7 +164,7 @@ XSyncHandler::XSyncHandler() xsync_error_base_(0), backing_store_sync_counter_(0), backing_store_sync_alarm_(0) { - Display* display = ui::GetXDisplay(); + XDisplay* display = gfx::GetXDisplay(); if (XSyncQueryExtension(display, &xsync_event_base_, &xsync_error_base_)) { @@ -191,7 +191,7 @@ XSyncHandler::~XSyncHandler() { if (loaded_extension_) gdk_window_remove_filter(NULL, &OnEventThunk, this); - XSync(ui::GetXDisplay(), False); + XSync(gfx::GetXDisplay(), False); while (!backing_store_events_.empty()) { // We delete the X11 resources we're holding onto. We don't run the // callbacks because we are shutting down. @@ -248,7 +248,7 @@ BackingStoreGtk::BackingStoreGtk(RenderWidgetHost* widget, void* visual, int depth) : BackingStore(widget, size), - display_(ui::GetXDisplay()), + display_(gfx::GetXDisplay()), shared_memory_support_(ui::QuerySharedMemorySupport(display_)), use_render_(ui::QueryRenderSupport(display_)), visual_(visual), diff --git a/chromium/content/browser/renderer_host/backing_store_gtk.h b/chromium/content/browser/renderer_host/backing_store_gtk.h index 0e81597c0b6..29776fcdf67 100644 --- a/chromium/content/browser/renderer_host/backing_store_gtk.h +++ b/chromium/content/browser/renderer_host/backing_store_gtk.h @@ -12,7 +12,7 @@ #include "build/build_config.h" #include "content/browser/renderer_host/backing_store.h" #include "content/common/content_export.h" -#include "ui/base/x/x11_util.h" +#include "ui/gfx/x/x11_types.h" namespace gfx { class Point; @@ -39,7 +39,7 @@ class CONTENT_EXPORT BackingStoreGtk : public BackingStore { virtual ~BackingStoreGtk(); - Display* display() const { return display_; } + XDisplay* display() const { return display_; } XID root_window() const { return root_window_; } // Copy from the server-side backing store to the target window @@ -79,7 +79,7 @@ class CONTENT_EXPORT BackingStoreGtk : public BackingStore { // This is the connection to the X server where this backing store will be // displayed. - Display* const display_; + XDisplay* 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. diff --git a/chromium/content/browser/renderer_host/backing_store_win.cc b/chromium/content/browser/renderer_host/backing_store_win.cc index 5ccafbb062b..8e761dd0c32 100644 --- a/chromium/content/browser/renderer_host/backing_store_win.cc +++ b/chromium/content/browser/renderer_host/backing_store_win.cc @@ -9,10 +9,10 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/common/content_switches.h" #include "skia/ext/platform_canvas.h" -#include "ui/base/win/dpi.h" #include "ui/gfx/gdi_util.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/size_conversions.h" +#include "ui/gfx/win/dpi.h" #include "ui/surface/transport_dib.h" namespace content { @@ -92,7 +92,7 @@ bool BackingStoreWin::ColorManagementEnabled() { size_t BackingStoreWin::MemorySize() { gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize(size(), - ui::win::GetDeviceScaleFactor())); + gfx::win::GetDeviceScaleFactor())); return size_in_pixels.GetArea() * (color_depth_ / 8); } @@ -104,6 +104,7 @@ void BackingStoreWin::PaintToBackingStore( float scale_factor, const base::Closure& completion_callback, bool* scheduled_completion_callback) { + TRACE_EVENT0("content", "BackingStoreWin::PaintToBackingStore"); *scheduled_completion_callback = false; gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize( size(), scale_factor)); @@ -155,6 +156,7 @@ void BackingStoreWin::PaintToBackingStore( bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformBitmap* output) { + TRACE_EVENT0("content", "BackingStoreWin::CopyFromBackingStore"); // TODO(kevers): Make sure this works with HiDPI backing stores. if (!output->Allocate(rect.width(), rect.height(), true)) return false; @@ -168,10 +170,11 @@ bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect, void BackingStoreWin::ScrollBackingStore(const gfx::Vector2d& delta, const gfx::Rect& clip_rect, const gfx::Size& view_size) { + TRACE_EVENT0("content", "BackingStoreWin::ScrollBackingStore"); // TODO(darin): this doesn't work if delta x() and y() are both non-zero! DCHECK(delta.x() == 0 || delta.y() == 0); - float scale = ui::win::GetDeviceScaleFactor(); + float scale = gfx::win::GetDeviceScaleFactor(); gfx::Rect screen_rect = gfx::ToEnclosingRect( gfx::ScaleRect(clip_rect, scale)); int dx = static_cast<int>(delta.x() * scale); diff --git a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc index 1bcc8223e97..e4563375681 100644 --- a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc +++ b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc @@ -26,9 +26,9 @@ bool BasicMouseWheelSmoothScrollGesture::ForwardInputEvents( if (pixels_scrolled_ >= pixels_to_scroll_) return false; - double position_delta = smooth_scroll_calculator_.GetScrollDelta( + float position_delta = synthetic_gesture_calculator_.GetDelta( now, - RenderWidgetHostImpl::From(host)->GetSyntheticScrollMessageInterval()); + RenderWidgetHostImpl::From(host)->GetSyntheticGestureMessageInterval()); WebKit::WebMouseWheelEvent event; diff --git a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h index adea7a30b74..ba127da05bd 100644 --- a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h +++ b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h @@ -6,14 +6,14 @@ #define CONTENT_BROWSER_RENDERER_HOST_BASIC_MOUSE_WHEEL_SMOOTH_SCROLL_GESTURE_ #include "base/time/time.h" -#include "content/browser/renderer_host/smooth_scroll_calculator.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/browser/renderer_host/synthetic_gesture_calculator.h" +#include "content/port/browser/synthetic_gesture.h" namespace content { class RenderWidgetHost; -class BasicMouseWheelSmoothScrollGesture : public SmoothScrollGesture { +class BasicMouseWheelSmoothScrollGesture : public SyntheticGesture { public: BasicMouseWheelSmoothScrollGesture(bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y); @@ -23,7 +23,7 @@ class BasicMouseWheelSmoothScrollGesture : public SmoothScrollGesture { private: virtual ~BasicMouseWheelSmoothScrollGesture(); - SmoothScrollCalculator smooth_scroll_calculator_; + SyntheticGestureCalculator synthetic_gesture_calculator_; bool scroll_down_; int pixels_scrolled_; diff --git a/chromium/content/browser/renderer_host/clipboard_message_filter.cc b/chromium/content/browser/renderer_host/clipboard_message_filter.cc index 0e2426bf224..e681188b937 100644 --- a/chromium/content/browser/renderer_host/clipboard_message_filter.cc +++ b/chromium/content/browser/renderer_host/clipboard_message_filter.cc @@ -11,7 +11,6 @@ #include "content/public/browser/browser_context.h" #include "ipc/ipc_message_macros.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/zlib/zlib.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/size.h" #include "url/gurl.h" @@ -199,19 +198,9 @@ void ClipboardMessageFilter::OnReadImageReply( const SkBitmap& bitmap, IPC::Message* reply_msg) { base::SharedMemoryHandle image_handle = base::SharedMemory::NULLHandle(); uint32 image_size = 0; - std::string reply_data; if (!bitmap.isNull()) { std::vector<unsigned char> png_data; - SkAutoLockPixels lock(bitmap); - if (gfx::PNGCodec::EncodeWithCompressionLevel( - static_cast<const unsigned char*>(bitmap.getPixels()), - gfx::PNGCodec::FORMAT_BGRA, - gfx::Size(bitmap.width(), bitmap.height()), - bitmap.rowBytes(), - false, - std::vector<gfx::PNGCodec::Comment>(), - Z_BEST_SPEED, - &png_data)) { + if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &png_data)) { base::SharedMemory buffer; if (buffer.CreateAndMapAnonymous(png_data.size())) { memcpy(buffer.memory(), vector_as_array(&png_data), png_data.size()); diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_mac.h b/chromium/content/browser/renderer_host/compositing_iosurface_mac.h index 674e4e512d6..623272be771 100644 --- a/chromium/content/browser/renderer_host/compositing_iosurface_mac.h +++ b/chromium/content/browser/renderer_host/compositing_iosurface_mac.h @@ -19,7 +19,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "media/base/video_frame.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" diff --git a/chromium/content/browser/renderer_host/compositor_impl_android.cc b/chromium/content/browser/renderer_host/compositor_impl_android.cc index 53c42e4277a..1e758c35a0d 100644 --- a/chromium/content/browser/renderer_host/compositor_impl_android.cc +++ b/chromium/content/browser/renderer_host/compositor_impl_android.cc @@ -22,10 +22,13 @@ #include "cc/output/compositor_frame.h" #include "cc/output/context_provider.h" #include "cc/output/output_surface.h" +#include "cc/resources/scoped_ui_resource.h" +#include "cc/resources/ui_resource_bitmap.h" #include "cc/trees/layer_tree_host.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/common/gpu/client/command_buffer_proxy_impl.h" +#include "content/common/gpu/client/context_provider_command_buffer.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" @@ -37,6 +40,7 @@ #include "third_party/khronos/GLES2/gl2ext.h" #include "ui/gfx/android/device_display_info.h" #include "ui/gfx/android/java_bitmap.h" +#include "webkit/common/gpu/context_provider_in_process.h" #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" namespace gfx { @@ -48,8 +52,9 @@ namespace { // Used for drawing directly to the screen. Bypasses resizing and swaps. class DirectOutputSurface : public cc::OutputSurface { public: - DirectOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : cc::OutputSurface(context3d.Pass()) { + DirectOutputSurface( + const scoped_refptr<cc::ContextProvider>& context_provider) + : cc::OutputSurface(context_provider) { capabilities_.adjust_deadline_for_parent = false; } @@ -57,23 +62,26 @@ class DirectOutputSurface : public cc::OutputSurface { surface_size_ = size; } virtual void SwapBuffers(cc::CompositorFrame*) OVERRIDE { - context3d()->shallowFlushCHROMIUM(); + context_provider_->Context3d()->shallowFlushCHROMIUM(); } }; // Used to override capabilities_.adjust_deadline_for_parent to false class OutputSurfaceWithoutParent : public cc::OutputSurface { public: - OutputSurfaceWithoutParent(scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : cc::OutputSurface(context3d.Pass()) { + OutputSurfaceWithoutParent( + const scoped_refptr< + content::ContextProviderCommandBuffer>& context_provider) + : cc::OutputSurface(context_provider) { capabilities_.adjust_deadline_for_parent = false; } virtual void SwapBuffers(cc::CompositorFrame* frame) OVERRIDE { - content::WebGraphicsContext3DCommandBufferImpl* command_buffer = - static_cast<content::WebGraphicsContext3DCommandBufferImpl*>(context3d()); + content::WebGraphicsContext3DCommandBufferImpl* command_buffer_context = + static_cast<content::WebGraphicsContext3DCommandBufferImpl*>( + context_provider_->Context3d()); content::CommandBufferProxyImpl* command_buffer_proxy = - command_buffer->GetCommandBufferProxy(); + command_buffer_context->GetCommandBufferProxy(); DCHECK(command_buffer_proxy); command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info); @@ -227,10 +235,11 @@ void CompositorImpl::SetSurface(jobject surface) { void CompositorImpl::SetVisible(bool visible) { if (!visible) { + ui_resource_map_.clear(); host_.reset(); + client_->UIResourcesAreInvalid(); } else if (!host_) { cc::LayerTreeSettings settings; - settings.compositor_name = "BrowserCompositor"; settings.refresh_rate = 60.0; settings.impl_side_painting = false; settings.allow_antialiasing = false; @@ -255,6 +264,9 @@ void CompositorImpl::SetVisible(bool visible) { host_->SetLayerTreeHostClientReady(); host_->SetViewportSize(size_); host_->set_has_transparent_background(has_transparent_background_); + // Need to recreate the UI resources because a new LayerTreeHost has been + // created. + client_->DidLoseUIResources(); } } @@ -286,6 +298,23 @@ bool CompositorImpl::CompositeAndReadback(void *pixels, const gfx::Rect& rect) { return false; } +cc::UIResourceId CompositorImpl::GenerateUIResource( + const cc::UIResourceBitmap& bitmap) { + if (!host_) + return 0; + scoped_ptr<cc::ScopedUIResource> ui_resource = + cc::ScopedUIResource::Create(host_.get(), bitmap); + cc::UIResourceId id = ui_resource->id(); + ui_resource_map_.set(id, ui_resource.Pass()); + return id; +} + +void CompositorImpl::DeleteUIResource(cc::UIResourceId resource_id) { + UIResourceMap::iterator it = ui_resource_map_.find(resource_id); + if (it != ui_resource_map_.end()) + ui_resource_map_.erase(it); +} + WebKit::WebGLId CompositorImpl::GenerateTexture(gfx::JavaBitmap& bitmap) { unsigned int texture_id = BuildBasicTexture(); WebKit::WebGraphicsContext3D* context = @@ -358,6 +387,41 @@ bool CompositorImpl::CopyTextureToBitmap(WebKit::WebGLId texture_id, return true; } +static scoped_ptr<WebGraphicsContext3DCommandBufferImpl> +CreateGpuProcessViewContext( + const WebKit::WebGraphicsContext3D::Attributes attributes, + int surface_id, + base::WeakPtr<CompositorImpl> compositor_impl) { + GpuChannelHostFactory* factory = BrowserGpuChannelHostFactory::instance(); + GURL url("chrome://gpu/Compositor::createContext3D"); + scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( + new WebGraphicsContext3DCommandBufferImpl(surface_id, + url, + factory, + compositor_impl)); + static const size_t kBytesPerPixel = 4; + gfx::DeviceDisplayInfo display_info; + size_t full_screen_texture_size_in_bytes = + display_info.GetDisplayHeight() * + display_info.GetDisplayWidth() * + kBytesPerPixel; + if (!context->Initialize( + attributes, + false, + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, + 64 * 1024, // command buffer size + 64 * 1024, // start transfer buffer size + 64 * 1024, // min transfer buffer size + std::min(3 * full_screen_texture_size_in_bytes, + kDefaultMaxTransferBufferSize), + 2 * 1024 * 1024 // mapped memory limit + )) { + LOG(ERROR) << "Failed to create 3D context for compositor."; + return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>(); + } + return context.Pass(); +} + scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface( bool fallback) { WebKit::WebGraphicsContext3D::Attributes attrs; @@ -365,46 +429,35 @@ scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface( attrs.noAutomaticFlushes = true; if (g_use_direct_gl) { - scoped_ptr<WebKit::WebGraphicsContext3D> context( - webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl:: - CreateViewContext(attrs, window_)); - if (!window_) { - return scoped_ptr<cc::OutputSurface>( - new DirectOutputSurface(context.Pass())); - } + using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; + scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl> context3d = + WebGraphicsContext3DInProcessCommandBufferImpl::CreateViewContext( + attrs, window_); + scoped_refptr<webkit::gpu::ContextProviderInProcess> context_provider = + webkit::gpu::ContextProviderInProcess::Create(context3d.Pass(), + "BrowserCompositor"); + + scoped_ptr<cc::OutputSurface> output_surface; + if (!window_) + output_surface.reset(new DirectOutputSurface(context_provider)); + else + output_surface.reset(new cc::OutputSurface(context_provider)); + return output_surface.Pass(); + } - return make_scoped_ptr(new cc::OutputSurface(context.Pass())); - } else { - DCHECK(window_ && surface_id_); - GpuChannelHostFactory* factory = BrowserGpuChannelHostFactory::instance(); - GURL url("chrome://gpu/Compositor::createContext3D"); - scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( - new WebGraphicsContext3DCommandBufferImpl(surface_id_, - url, - factory, - weak_factory_.GetWeakPtr())); - static const size_t kBytesPerPixel = 4; - gfx::DeviceDisplayInfo display_info; - size_t full_screen_texture_size_in_bytes = - display_info.GetDisplayHeight() * - display_info.GetDisplayWidth() * - kBytesPerPixel; - if (!context->Initialize( - attrs, - false, - CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, - 64 * 1024, // command buffer size - 64 * 1024, // start transfer buffer size - 64 * 1024, // min transfer buffer size - std::min(3 * full_screen_texture_size_in_bytes, - kDefaultMaxTransferBufferSize))) { - LOG(ERROR) << "Failed to create 3D context for compositor."; - return scoped_ptr<cc::OutputSurface>(); - } - return scoped_ptr<cc::OutputSurface>( - new OutputSurfaceWithoutParent( - context.PassAs<WebKit::WebGraphicsContext3D>())); + DCHECK(window_); + DCHECK(surface_id_); + + scoped_refptr<ContextProviderCommandBuffer> context_provider = + ContextProviderCommandBuffer::Create(CreateGpuProcessViewContext( + attrs, surface_id_, weak_factory_.GetWeakPtr()), "BrowserCompositor"); + if (!context_provider.get()) { + LOG(ERROR) << "Failed to create 3D context for compositor."; + return scoped_ptr<cc::OutputSurface>(); } + + return scoped_ptr<cc::OutputSurface>( + new OutputSurfaceWithoutParent(context_provider)); } void CompositorImpl::OnLostResources() { diff --git a/chromium/content/browser/renderer_host/compositor_impl_android.h b/chromium/content/browser/renderer_host/compositor_impl_android.h index 845e830aad4..58e44c89449 100644 --- a/chromium/content/browser/renderer_host/compositor_impl_android.h +++ b/chromium/content/browser/renderer_host/compositor_impl_android.h @@ -7,8 +7,10 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "cc/resources/ui_resource_client.h" #include "cc/trees/layer_tree_host_client.h" #include "content/browser/renderer_host/image_transport_factory_android.h" #include "content/common/content_export.h" @@ -21,6 +23,7 @@ namespace cc { class InputHandlerClient; class Layer; class LayerTreeHost; +class ScopedUIResource; } namespace content { @@ -60,6 +63,9 @@ class CONTENT_EXPORT CompositorImpl void *pixels, const gfx::Rect& rect) OVERRIDE; virtual void SetNeedsRedraw() OVERRIDE; virtual void Composite() OVERRIDE; + virtual cc::UIResourceId GenerateUIResource( + const cc::UIResourceBitmap& bitmap) OVERRIDE; + virtual void DeleteUIResource(cc::UIResourceId resource_id) OVERRIDE; virtual WebKit::WebGLId GenerateTexture(gfx::JavaBitmap& bitmap) OVERRIDE; virtual WebKit::WebGLId GenerateCompressedTexture( gfx::Size& size, int data_size, void* data) OVERRIDE; @@ -117,6 +123,10 @@ class CONTENT_EXPORT CompositorImpl scoped_refptr<cc::ContextProvider> null_offscreen_context_provider_; + typedef base::ScopedPtrHashMap<cc::UIResourceId, cc::ScopedUIResource> + UIResourceMap; + UIResourceMap ui_resource_map_; + DISALLOW_COPY_AND_ASSIGN(CompositorImpl); }; diff --git a/chromium/content/browser/renderer_host/frame_memory_manager.cc b/chromium/content/browser/renderer_host/frame_memory_manager.cc new file mode 100644 index 00000000000..708bddfa61b --- /dev/null +++ b/chromium/content/browser/renderer_host/frame_memory_manager.cc @@ -0,0 +1,66 @@ +// Copyright 2013 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/frame_memory_manager.h" + +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/sys_info.h" + +namespace content { + +namespace { + +size_t MaxNumberOfSavedFrames() { + return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256)); +} + +} // namespace + +FrameMemoryManager* FrameMemoryManager::GetInstance() { + return Singleton<FrameMemoryManager>::get(); +} + +void FrameMemoryManager::AddFrame(FrameContainer* frame, bool visible) { + RemoveFrame(frame); + if (visible) + visible_frames_.insert(frame); + else + hidden_frames_.push_front(frame); + CullHiddenFrames(); +} + +void FrameMemoryManager::RemoveFrame(FrameContainer* frame) { + visible_frames_.erase(frame); + hidden_frames_.remove(frame); +} + +void FrameMemoryManager::SetFrameVisibility(FrameContainer* frame, + bool visible) { + if (visible) { + hidden_frames_.remove(frame); + visible_frames_.insert(frame); + } else { + visible_frames_.erase(frame); + hidden_frames_.push_front(frame); + CullHiddenFrames(); + } +} + +FrameMemoryManager::FrameMemoryManager() {} + +FrameMemoryManager::~FrameMemoryManager() {} + +void FrameMemoryManager::CullHiddenFrames() { + while (!hidden_frames_.empty() && + hidden_frames_.size() + visible_frames_.size() > + MaxNumberOfSavedFrames()) { + size_t old_size = hidden_frames_.size(); + // Should remove self from list. + hidden_frames_.back()->ReleaseCurrentFrame(); + DCHECK_EQ(hidden_frames_.size() + 1, old_size); + } +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/frame_memory_manager.h b/chromium/content/browser/renderer_host/frame_memory_manager.h new file mode 100644 index 00000000000..ce3fc258226 --- /dev/null +++ b/chromium/content/browser/renderer_host/frame_memory_manager.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_FRAME_MEMORY_MANAGER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ + +#include <list> +#include <set> + +#include "base/basictypes.h" + +template <typename T> struct DefaultSingletonTraits; + +namespace content { + +class FrameContainer { + public: + virtual void ReleaseCurrentFrame() = 0; +}; + +class FrameMemoryManager { + public: + static FrameMemoryManager* GetInstance(); + + void AddFrame(FrameContainer*, bool visible); + void RemoveFrame(FrameContainer*); + void SetFrameVisibility(FrameContainer*, bool visible); + + private: + FrameMemoryManager(); + ~FrameMemoryManager(); + void CullHiddenFrames(); + friend struct DefaultSingletonTraits<FrameMemoryManager>; + + std::set<FrameContainer*> visible_frames_; + std::list<FrameContainer*> hidden_frames_; + + DISALLOW_COPY_AND_ASSIGN(FrameMemoryManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ diff --git a/chromium/content/browser/renderer_host/generic_touch_gesture_android.cc b/chromium/content/browser/renderer_host/generic_touch_gesture_android.cc new file mode 100644 index 00000000000..74080cd408c --- /dev/null +++ b/chromium/content/browser/renderer_host/generic_touch_gesture_android.cc @@ -0,0 +1,62 @@ +// Copyright 2013 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/generic_touch_gesture_android.h" + +#include "base/debug/trace_event.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "jni/GenericTouchGesture_jni.h" + +namespace { +bool g_jni_initialized = false; + +void RegisterNativesIfNeeded(JNIEnv* env) { + if (!g_jni_initialized) { + content::RegisterNativesImpl(env); + g_jni_initialized = true; + } +} +} // namespace + +namespace content { + +GenericTouchGestureAndroid::GenericTouchGestureAndroid( + RenderWidgetHost* rwh, + base::android::ScopedJavaLocalRef<jobject> java_touch_gesture) + : has_started_(false), + has_finished_(false), + rwh_(rwh), + java_touch_gesture_(java_touch_gesture) { + JNIEnv* env = base::android::AttachCurrentThread(); + RegisterNativesIfNeeded(env); +} + +GenericTouchGestureAndroid::~GenericTouchGestureAndroid() { +} + +bool GenericTouchGestureAndroid::ForwardInputEvents( + base::TimeTicks now, RenderWidgetHost* host) { + if (!has_started_) { + has_started_ = true; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_GenericTouchGesture_start( + env, java_touch_gesture_.obj(), reinterpret_cast<intptr_t>(this)); + } + + return !has_finished_; +} + +float GenericTouchGestureAndroid::GetDelta( + JNIEnv* env, jobject obj, float scale) { + return synthetic_gesture_calculator_.GetDelta( + base::TimeTicks::Now(), + RenderWidgetHostImpl::From(rwh_)->GetSyntheticGestureMessageInterval()) * + scale; +} + +void GenericTouchGestureAndroid::SetHasFinished(JNIEnv* env, jobject obj) { + has_finished_ = true; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/generic_touch_gesture_android.h b/chromium/content/browser/renderer_host/generic_touch_gesture_android.h new file mode 100644 index 00000000000..030239b1c30 --- /dev/null +++ b/chromium/content/browser/renderer_host/generic_touch_gesture_android.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_GENERIC_TOUCH_GESTURE_ANDROID_H_ +#define CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_ + +#include "base/android/jni_android.h" +#include "base/time/time.h" +#include "content/browser/renderer_host/synthetic_gesture_calculator.h" +#include "content/port/browser/synthetic_gesture.h" + +namespace content { + +class ContentViewCore; +class RenderWidgetHost; + +class GenericTouchGestureAndroid : public SyntheticGesture { + public: + GenericTouchGestureAndroid( + RenderWidgetHost* rwh, + base::android::ScopedJavaLocalRef<jobject> java_scroller); + + // Called by the java side once the TimeAnimator ticks. + float GetDelta(JNIEnv* env, jobject obj, float scale); + void SetHasFinished(JNIEnv* env, jobject obj); + + // SmoothScrollGesture + virtual bool ForwardInputEvents(base::TimeTicks now, + RenderWidgetHost* host) OVERRIDE; + + private: + virtual ~GenericTouchGestureAndroid(); + + SyntheticGestureCalculator synthetic_gesture_calculator_; + + bool has_started_; + bool has_finished_; + + RenderWidgetHost* rwh_; + base::android::ScopedJavaGlobalRef<jobject> java_touch_gesture_; + + DISALLOW_COPY_AND_ASSIGN(GenericTouchGestureAndroid); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_ diff --git a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc b/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc index 55d2e76bb30..65dc54524c2 100644 --- a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc +++ b/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc @@ -439,7 +439,7 @@ void GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event, // Unlike a Char event, an IME event is NOT dispatched to onkeypress() // handlers or autofill. host->ImeConfirmComposition( - commit_text_,ui::Range::InvalidRange(),false); + commit_text_,gfx::Range::InvalidRange(),false); // Set this flag to false, as this composition session has been // finished. is_composing_text_ = false; @@ -477,7 +477,7 @@ void GtkIMContextWrapper::ConfirmComposition() { if (host_view_->GetRenderWidgetHost()) { RenderWidgetHostImpl::From( host_view_->GetRenderWidgetHost())->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); } // Reset the input method. @@ -503,7 +503,7 @@ void GtkIMContextWrapper::HandleCommit(const string16& text) { SendFakeCompositionKeyEvent(WebKit::WebInputEvent::RawKeyDown); RenderWidgetHostImpl::From( host_view_->GetRenderWidgetHost())->ImeConfirmComposition( - text, ui::Range::InvalidRange(), false); + text, gfx::Range::InvalidRange(), false); SendFakeCompositionKeyEvent(WebKit::WebInputEvent::KeyUp); } } @@ -532,7 +532,7 @@ void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, // TODO(suzhe): due to a bug of webkit, we currently can't use selection range // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 - composition_.selection = ui::Range(cursor_position); + composition_.selection = gfx::Range(cursor_position); // In case we are using a buggy input method which doesn't fire // "preedit_start" signal. diff --git a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc b/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc index 93e20778607..861491111b5 100644 --- a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc +++ b/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc @@ -9,7 +9,7 @@ #include "base/logging.h" #include "content/browser/renderer_host/gtk_plugin_container.h" #include "content/common/webplugin_geometry.h" -#include "ui/base/gtk/gtk_compat.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/gtk_util.h" namespace content { diff --git a/chromium/content/browser/renderer_host/gtk_window_utils.cc b/chromium/content/browser/renderer_host/gtk_window_utils.cc index 979c58d05a5..f05cdca3b66 100644 --- a/chromium/content/browser/renderer_host/gtk_window_utils.cc +++ b/chromium/content/browser/renderer_host/gtk_window_utils.cc @@ -10,10 +10,11 @@ #include <vector> -#include "ui/base/gtk/gtk_compat.h" +#include "third_party/WebKit/public/web/WebScreenInfo.h" #include "ui/base/x/x11_util.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/rect.h" -#include "third_party/WebKit/public/web/WebScreenInfo.h" +#include "ui/gfx/x/x11_types.h" namespace content { @@ -44,7 +45,7 @@ gfx::Rect GetWorkArea(Window window) { property[start_index + 2], property[start_index + 3]); } -WebKit::WebScreenInfo GetScreenInfo(Display* display, int screenNumber) { +WebKit::WebScreenInfo GetScreenInfo(XDisplay* display, int screenNumber) { // XDisplayWidth() and XDisplayHeight() return cached values. To ensure that // we return the correct dimensions after the screen is resized, query the // root window's geometry each time. diff --git a/chromium/content/browser/renderer_host/image_transport_factory_android.cc b/chromium/content/browser/renderer_host/image_transport_factory_android.cc index 39071c9bec3..bc0d365d249 100644 --- a/chromium/content/browser/renderer_host/image_transport_factory_android.cc +++ b/chromium/content/browser/renderer_host/image_transport_factory_android.cc @@ -128,7 +128,9 @@ CmdBufferImageTransportFactory::CmdBufferImageTransportFactory() { 64 * 1024, // starting buffer size 64 * 1024, // min buffer size std::min(3 * full_screen_texture_size_in_bytes, - kDefaultMaxTransferBufferSize)); + kDefaultMaxTransferBufferSize), + WebGraphicsContext3DCommandBufferImpl::kNoLimit + ); if (context_->makeContextCurrent()) context_->pushGroupMarkerEXT( diff --git a/chromium/content/browser/renderer_host/ime_adapter_android.cc b/chromium/content/browser/renderer_host/ime_adapter_android.cc index 588c5ada8e6..da14d7a3d5d 100644 --- a/chromium/content/browser/renderer_host/ime_adapter_android.cc +++ b/chromium/content/browser/renderer_host/ime_adapter_android.cc @@ -174,7 +174,7 @@ void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text) { return; string16 text16 = ConvertJavaStringToUTF16(env, text); - rwhi->ImeConfirmComposition(text16, ui::Range::InvalidRange(), false); + rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false); } void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) { @@ -182,7 +182,7 @@ void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) { if (!rwhi) return; - rwhi->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), true); + rwhi->ImeConfirmComposition(string16(), gfx::Range::InvalidRange(), true); } void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) { diff --git a/chromium/content/browser/renderer_host/input/OWNERS b/chromium/content/browser/renderer_host/input/OWNERS index 4dabaf0da2c..4893edbdf02 100644 --- a/chromium/content/browser/renderer_host/input/OWNERS +++ b/chromium/content/browser/renderer_host/input/OWNERS @@ -1,2 +1,3 @@ -nduca@chromium.org aelias@chromium.org +jdduke@chromium.org +nduca@chromium.org diff --git a/chromium/content/browser/renderer_host/input/browser_input_event.cc b/chromium/content/browser/renderer_host/input/browser_input_event.cc new file mode 100644 index 00000000000..276245e3526 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/browser_input_event.cc @@ -0,0 +1,47 @@ +// Copyright 2013 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/input/browser_input_event.h" + +#include "content/common/input/web_input_event_payload.h" + +namespace content { + +BrowserInputEvent::BrowserInputEvent(BrowserInputEventClient* client) + : client_(client) {} + +BrowserInputEvent::~BrowserInputEvent() {} + +scoped_ptr<BrowserInputEvent> BrowserInputEvent::Create( + int64 id, + scoped_ptr<InputEvent::Payload> payload, + BrowserInputEventClient* client) { + DCHECK(client); + scoped_ptr<BrowserInputEvent> event(new BrowserInputEvent(client)); + event->Initialize(id, payload.Pass()); + return event.Pass(); +} + +void BrowserInputEvent::OnDispatched( + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup_events) { + DCHECK(followup_events); + + bool event_consumed = + disposition == INPUT_EVENT_MAIN_THREAD_CONSUMED || + disposition == INPUT_EVENT_IMPL_THREAD_CONSUMED || + disposition == INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED; + + if (!event_consumed && CanCreateFollowupEvents()) + client_->OnDispatched(*this, disposition, followup_events); + else + client_->OnDispatched(*this, disposition); +} + +bool BrowserInputEvent::CanCreateFollowupEvents() const { + return payload()->GetType() == InputEvent::Payload::WEB_INPUT_EVENT && + WebInputEventPayload::Cast(payload())->CanCreateFollowupEvents(); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/browser_input_event.h b/chromium/content/browser/renderer_host/input/browser_input_event.h new file mode 100644 index 00000000000..6022d80ecd2 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/browser_input_event.h @@ -0,0 +1,69 @@ +// Copyright 2013 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_INPUT_BROWSER_INPUT_EVENT_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_vector.h" +#include "content/common/input/input_event.h" +#include "content/common/input/input_event_disposition.h" + +namespace content { + +class BrowserInputEvent; + +// Provides customized dispatch response for BrowserInputEvents. +class BrowserInputEventClient { + public: + virtual ~BrowserInputEventClient() {} + + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) {} + + // Called if the event went unconsumed and can create followup events. Any + // events added to |followup| by the client will be inserted into the + // current input event stream. |followup| will never be NULL. + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) {} +}; + +// Augmented InputEvent allowing customized dispatch response in the browser. +class CONTENT_EXPORT BrowserInputEvent : public InputEvent { + public: + // |client| is assumed to be non-NULL. + static scoped_ptr<BrowserInputEvent> Create( + int64 id, + scoped_ptr<InputEvent::Payload> payload, + BrowserInputEventClient* client); + + template <typename PayloadType> + static scoped_ptr<BrowserInputEvent> Create(int64 id, + scoped_ptr<PayloadType> payload, + BrowserInputEventClient* client) { + return Create(id, payload.template PassAs<InputEvent::Payload>(), client); + } + + virtual ~BrowserInputEvent(); + + // |followup_events| must not be NULL, and will only be modified if the + // event went unconsumed and can create followup events. + void OnDispatched(InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup_events); + + protected: + explicit BrowserInputEvent(BrowserInputEventClient* client); + + bool CanCreateFollowupEvents() const; + + private: + BrowserInputEventClient* client_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_ diff --git a/chromium/content/browser/renderer_host/input/buffered_input_router.cc b/chromium/content/browser/renderer_host/input/buffered_input_router.cc new file mode 100644 index 00000000000..97b565311d5 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/buffered_input_router.cc @@ -0,0 +1,336 @@ +// Copyright 2013 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/input/buffered_input_router.h" + +#include "base/auto_reset.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" +#include "content/browser/renderer_host/input/input_queue.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/common/input_messages.h" +#include "content/common/view_messages.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "content/public/browser/user_metrics.h" + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; + +namespace content { + +BufferedInputRouter::BufferedInputRouter(IPC::Sender* sender, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id) + : client_(client), + ack_handler_(ack_handler), + sender_(sender), + routing_id_(routing_id), + has_touch_handler_(false), + queued_touch_count_(0), + input_queue_override_(NULL), + next_input_id_(1), + in_flight_packet_id_(0) { + input_queue_.reset(new InputQueue(this)); +} + +BufferedInputRouter::~BufferedInputRouter() {} + +void BufferedInputRouter::Flush() { + TRACE_EVENT0("input", "BufferedInputRouter::Flush"); + DCHECK_EQ(0, in_flight_packet_id_); + input_queue_->BeginFlush(); +} + +bool BufferedInputRouter::SendInput(scoped_ptr<IPC::Message> message) { + DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); + DCHECK(message->type() != InputMsg_HandleEventPacket::ID); + DCHECK(message->type() != InputMsg_HandleInputEvent::ID); + input_queue_->QueueEvent(BrowserInputEvent::Create( + NextInputID(), IPCInputEventPayload::Create(message.Pass()), this)); + return true; +} + +void BufferedInputRouter::SendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) { + if (!client_->OnSendMouseEvent(mouse_event)) + return; + // TODO(jdduke): Coalescing, http://crbug.com/289520 + QueueWebEvent(mouse_event.event, mouse_event.latency, false); +} + +void BufferedInputRouter::SendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) { + if (!client_->OnSendWheelEvent(wheel_event)) + return; + QueueWebEvent(wheel_event.event, wheel_event.latency, false); +} + +void BufferedInputRouter::SendKeyboardEvent( + const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info) { + bool is_shortcut = false; + if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut)) + return; + int64 event_id = QueueWebEvent(key_event, latency_info, is_shortcut); + if (event_id) { + DCHECK(queued_key_map_.find(event_id) == queued_key_map_.end()); + queued_key_map_[event_id] = key_event; + } +} + +void BufferedInputRouter::SendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) { + if (!client_->OnSendGestureEvent(gesture_event)) + return; + QueueWebEvent(gesture_event.event, gesture_event.latency, false); +} + +void BufferedInputRouter::SendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) { + if (!client_->OnSendTouchEvent(touch_event)) + return; + if (QueueWebEvent(touch_event.event, touch_event.latency, false)) + ++queued_touch_count_; +} + +void BufferedInputRouter::SendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) { + if (!client_->OnSendMouseEventImmediately(mouse_event)) + return; + QueueWebEvent(mouse_event.event, mouse_event.latency, false); +} + +void BufferedInputRouter::SendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) { + if (!client_->OnSendTouchEventImmediately(touch_event)) + return; + QueueWebEvent(touch_event.event, touch_event.latency, false); +} + +void BufferedInputRouter::SendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) { + if (!client_->OnSendGestureEventImmediately(gesture_event)) + return; + QueueWebEvent(gesture_event.event, gesture_event.latency, false); +} + +void BufferedInputRouter::Deliver(const EventPacket& packet) { + TRACE_EVENT2("input", "BufferedInputRouter::DeliverPacket", + "id", packet.id(), + "events", packet.size()); + DCHECK(packet.id()); + DCHECK(!in_flight_packet_id_); + if (!sender_->Send(new InputMsg_HandleEventPacket( + routing_id_, packet, InputEventDispositions()))) { + return; + } + + in_flight_packet_id_ = packet.id(); + client_->IncrementInFlightEventCount(); +} + +void BufferedInputRouter::DidFinishFlush() { + TRACE_EVENT0("input", "BufferedInputRouter::DidFinishFlush"); + client_->DidFlush(); +} + +void BufferedInputRouter::SetNeedsFlush() { + TRACE_EVENT0("input", "BufferedInputRouter::SetNeedsFlush"); + client_->SetNeedsFlush(); +} + +void BufferedInputRouter::OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) { + // Only WebInputEvents currently have ack response. + if (event.payload()->GetType() != InputEvent::Payload::WEB_INPUT_EVENT) + return; + + const WebInputEventPayload* web_payload = + WebInputEventPayload::Cast(event.payload()); + + OnWebInputEventAck(event.id(), + *web_payload->web_event(), + web_payload->latency_info(), + ToAckState(disposition), + true); +} + +void BufferedInputRouter::OnDispatched( + const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) { + DCHECK(followup); + DCHECK_NE(INPUT_EVENT_ACK_STATE_CONSUMED, ToAckState(disposition)); + + // Events sent to the router within this scope will be added to |followup|. + base::AutoReset<ScopedVector<BrowserInputEvent>*> input_queue_override( + &input_queue_override_, followup); + + OnDispatched(event, disposition); +} + +bool BufferedInputRouter::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + bool message_is_ok = true; + IPC_BEGIN_MESSAGE_MAP_EX(BufferedInputRouter, message, message_is_ok) + IPC_MESSAGE_HANDLER(InputHostMsg_HandleEventPacket_ACK, OnEventPacketAck) + IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, + OnHasTouchEventHandlers) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + if (!message_is_ok) + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); + + return handled; +} + +const NativeWebKeyboardEvent* + BufferedInputRouter::GetLastKeyboardEvent() const { + return queued_key_map_.empty() ? NULL : &queued_key_map_.begin()->second; +} + +bool BufferedInputRouter::ShouldForwardTouchEvent() const { + return has_touch_handler_ && queued_touch_count_ > 0; +} + +bool BufferedInputRouter::ShouldForwardGestureEvent( + const GestureEventWithLatencyInfo& touch_event) const { + return true; +} + +void BufferedInputRouter::OnWebInputEventAck( + int64 event_id, + const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + InputEventAckState acked_result, + bool ack_from_input_queue) { + if (WebInputEvent::isKeyboardEventType(web_event.type)) { + if (ack_from_input_queue) { + KeyMap::iterator key_it = queued_key_map_.find(event_id); + DCHECK(key_it != queued_key_map_.end()); + NativeWebKeyboardEvent key_event = key_it->second; + queued_key_map_.erase(key_it); + ack_handler_->OnKeyboardEventAck(key_event, acked_result); + } else { + DCHECK_EQ(0, event_id); + ack_handler_->OnKeyboardEventAck( + static_cast<const NativeWebKeyboardEvent&>(web_event), acked_result); + } + // WARNING: This BufferedInputRouter can be deallocated at this point + // (i.e. in the case of Ctrl+W, where the call to + // HandleKeyboardEvent destroys this BufferedInputRouter). + } else if (web_event.type == WebInputEvent::MouseWheel) { + ack_handler_->OnWheelEventAck( + static_cast<const WebMouseWheelEvent&>(web_event), acked_result); + } else if (WebInputEvent::isTouchEventType(web_event.type)) { + if (ack_from_input_queue) { + DCHECK_GT(queued_touch_count_, 0); + --queued_touch_count_; + } + ack_handler_->OnTouchEventAck( + TouchEventWithLatencyInfo(static_cast<const WebTouchEvent&>(web_event), + latency_info), + acked_result); + } else if (WebInputEvent::isGestureEventType(web_event.type)) { + ack_handler_->OnGestureEventAck( + static_cast<const WebGestureEvent&>(web_event), acked_result); + } else + NOTREACHED() << "Unexpected WebInputEvent in OnWebInputEventAck"; +} + +void BufferedInputRouter::OnEventPacketAck( + int64 packet_id, + const InputEventDispositions& dispositions) { + TRACE_EVENT2("input", "BufferedInputRouter::OnEventPacketAck", + "id", packet_id, + "dispositions", dispositions.size()); + if (!in_flight_packet_id_ || packet_id != in_flight_packet_id_) { + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); + return; + } + + in_flight_packet_id_ = 0; + client_->DecrementInFlightEventCount(); + + InputQueue::AckResult ack_result = + input_queue_->OnEventPacketAck(packet_id, dispositions); + if (ack_result == InputQueue::ACK_UNEXPECTED) + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); + else if (ack_result == InputQueue::ACK_INVALID) + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); +} + +void BufferedInputRouter::OnHasTouchEventHandlers(bool has_handlers) { + if (has_touch_handler_ == has_handlers) + return; + has_touch_handler_ = has_handlers; + client_->OnHasTouchEventHandlers(has_handlers); +} + +int64 BufferedInputRouter::QueueWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + bool is_key_shortcut) { + TRACE_EVENT0("input", "BufferedInputRouter::QueueWebEvent"); + + if (FilterWebEvent(web_event, latency_info)) { + TRACE_EVENT_INSTANT0("input", + "BufferedInputRouter::QueueWebEvent::Filtered", + TRACE_EVENT_SCOPE_THREAD); + return 0; + } + + int64 event_id = NextInputID(); + scoped_ptr<BrowserInputEvent> event = BrowserInputEvent::Create( + event_id, + WebInputEventPayload::Create(web_event, + latency_info, + is_key_shortcut), + this); + + // The presence of |input_queue_override_| implies that we are in the + // scope of |OnInputEventDispatched()| with an event can create followup. + if (input_queue_override_) + input_queue_override_->push_back(event.release()); + else + input_queue_->QueueEvent(event.Pass()); + + return event_id; +} + +bool BufferedInputRouter::FilterWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info) { + // Perform optional, synchronous event handling, sending ACK messages for + // processed events, or proceeding as usual. + InputEventAckState filter_ack = + client_->FilterInputEvent(web_event, latency_info); + switch (filter_ack) { + // Send the ACK and early exit. + case INPUT_EVENT_ACK_STATE_CONSUMED: + case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: + OnWebInputEventAck(0, web_event, latency_info, filter_ack, false); + // WARNING: |this| may be deleted at this point. + return true; + + // Drop the event. + case INPUT_EVENT_ACK_STATE_UNKNOWN: + return true; + + // Proceed as normal. + case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: + default: + break; + }; + + return false; +} + +int64 BufferedInputRouter::NextInputID() { return next_input_id_++; } + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/buffered_input_router.h b/chromium/content/browser/renderer_host/input/buffered_input_router.h new file mode 100644 index 00000000000..a9802a68198 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/buffered_input_router.h @@ -0,0 +1,144 @@ +// Copyright 2013 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_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_ +#define CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_queue.h" +#include "content/browser/renderer_host/input/input_queue_client.h" +#include "content/browser/renderer_host/input/input_router.h" + +namespace IPC { +class Sender; +} + +namespace content { + +class InputAckHandler; +class RenderProcessHost; +class RenderWidgetHostImpl; + +// Batches input events into EventPackets using a general input queue. Packets +// are sent the renderer on |Flush()|, called in response to flush requests. +class CONTENT_EXPORT BufferedInputRouter + : public NON_EXPORTED_BASE(BrowserInputEventClient), + public NON_EXPORTED_BASE(InputQueueClient), + public NON_EXPORTED_BASE(InputRouter) { + public: + // |sender|, |client| and |ack_handler| must outlive the BufferedInputRouter. + BufferedInputRouter(IPC::Sender* sender, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id); + virtual ~BufferedInputRouter(); + + // InputRouter + virtual void Flush() OVERRIDE; + virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE; + + // Certain unhandled input event acks may create follow-up events, e.g., + // TouchEvent -> GestureEvent. If these follow-up events are sent to the + // router synchronously from the original event's |OnDispatched()| ack, they + // will be inserted into the current input flush stream. + virtual void SendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual void SendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE; + virtual void SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info) OVERRIDE; + virtual void SendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual void SendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual void SendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual void SendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual void SendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual const NativeWebKeyboardEvent* GetLastKeyboardEvent() const OVERRIDE; + virtual bool ShouldForwardTouchEvent() const OVERRIDE; + virtual bool ShouldForwardGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE; + + // InputQueueClient + virtual void Deliver(const EventPacket& packet) OVERRIDE; + virtual void DidFinishFlush() OVERRIDE; + virtual void SetNeedsFlush() OVERRIDE; + + // BrowserInputEventClient + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) OVERRIDE; + // Events delivered to the router within the scope of + // |OnDispatched()| will be added to |followup|. + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) OVERRIDE; + + // IPC::Receiver + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + protected: + void OnWebInputEventAck(int64 event_id, + const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + InputEventAckState acked_result, + bool ack_from_input_queue); + void OnEventPacketAck(int64 packet_id, + const InputEventDispositions& dispositions); + void OnHasTouchEventHandlers(bool has_handlers); + + // Returns the non-zero ID associated with the |InputEvent| added to the + // |input_queue_|. If the event was dropped or filtered, returns 0. + int64 QueueWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + bool is_key_shortcut); + // Used by |QueueWebEvent()|; returns true if an event was filtered and should + // not be added to the |input_queue_|. + bool FilterWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info); + + // Generates a monotonically increasing sequences of id's, starting with 1. + int64 NextInputID(); + + const InputQueue* input_queue() const { return input_queue_.get(); } + + private: + InputRouterClient* client_; + InputAckHandler* ack_handler_; + IPC::Sender* sender_; + int routing_id_; + + scoped_ptr<InputQueue> input_queue_; + + // TODO(jdduke): Remove when we can properly serialize NativeWebKeyboardEvent. + // Alternatively, attach WebInputEvents to InputEvents but don't serialize. + typedef std::map<int64, NativeWebKeyboardEvent> KeyMap; + KeyMap queued_key_map_; + + // Necessary for |ShouldForwardTouchEvent()|. + bool has_touch_handler_; + int queued_touch_count_; + + // This is non-NULL ONLY in the scope of OnInputEventAck(event, injector). + ScopedVector<BrowserInputEvent>* input_queue_override_; + + // Used to assign unique ID's to each InputEvent that is generated. + int64 next_input_id_; + + // 0 if there no in-flight EventPacket. + int64 in_flight_packet_id_; + + DISALLOW_COPY_AND_ASSIGN(BufferedInputRouter); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_ diff --git a/chromium/content/browser/renderer_host/input/buffered_input_router_unittest.cc b/chromium/content/browser/renderer_host/input/buffered_input_router_unittest.cc new file mode 100644 index 00000000000..4d63444fef8 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/buffered_input_router_unittest.cc @@ -0,0 +1,320 @@ +// Copyright 2013 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 "content/browser/renderer_host/input/buffered_input_router.h" +#include "content/browser/renderer_host/input/input_router_unittest.h" +#include "content/common/input/event_packet.h" +#include "content/common/input_messages.h" +#include "content/common/view_messages.h" +#include "testing/gtest/include/gtest/gtest.h" + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +class TestBufferedInputRouter : public BufferedInputRouter { + public: + TestBufferedInputRouter(IPC::Sender* sender, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id) + : BufferedInputRouter(sender, client, ack_handler, routing_id) {} + + + size_t QueuedEventCount() const { return input_queue()->QueuedEventCount(); } +}; + +class BufferedInputRouterTest : public InputRouterTest { + public: + BufferedInputRouterTest() {} + virtual ~BufferedInputRouterTest() {} + + protected: + // InputRouterTest + virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* handler, + int routing_id) OVERRIDE { + return scoped_ptr<InputRouter>( + new TestBufferedInputRouter(process, client, handler, routing_id)); + } + + bool FinishFlush(const InputEventDispositions& dispositions) { + if (!process_->sink().message_count()) + return false; + IPC::Message message(*process_->sink().GetMessageAt(0)); + process_->sink().ClearMessages(); + + InputMsg_HandleEventPacket::Param param; + InputMsg_HandleEventPacket::Read(&message, ¶m); + EventPacket& packet = param.a; + + return SendEventPacketACK(packet.id(), dispositions); + } + + bool FinishFlush(InputEventDisposition disposition) { + if (!process_->sink().message_count()) + return false; + IPC::Message message(*process_->sink().GetMessageAt(0)); + process_->sink().ClearMessages(); + + InputMsg_HandleEventPacket::Param param; + InputMsg_HandleEventPacket::Read(&message, ¶m); + EventPacket& packet = param.a; + + return SendEventPacketACK( + packet.id(), InputEventDispositions(packet.size(), disposition)); + } + + bool SendEventPacketACK(int id, const InputEventDispositions& dispositions) { + return input_router_->OnMessageReceived( + InputHostMsg_HandleEventPacket_ACK(0, id, dispositions)); + } + + size_t QueuedEventCount() const { + return buffered_input_router()->QueuedEventCount(); + } + + TestBufferedInputRouter* buffered_input_router() const { + return static_cast<TestBufferedInputRouter*>(input_router_.get()); + } +}; + +TEST_F(BufferedInputRouterTest, InputEventsProperlyQueued) { + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Redo(MSG_ROUTING_NONE)))); + EXPECT_EQ(1U, QueuedEventCount()); + + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Cut(MSG_ROUTING_NONE)))); + EXPECT_EQ(2U, QueuedEventCount()); + + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Copy(MSG_ROUTING_NONE)))); + EXPECT_EQ(3U, QueuedEventCount()); + + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Paste(MSG_ROUTING_NONE)))); + EXPECT_EQ(4U, QueuedEventCount()); +} + +#define SCOPED_EXPECT(CALL, MESSAGE) { SCOPED_TRACE(MESSAGE); CALL; } + +TEST_F(BufferedInputRouterTest, ClientOnSendEventCalled) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(1U, QueuedEventCount()); + + SimulateWheelEvent(5, 0, 0, false); + EXPECT_EQ(2U, QueuedEventCount()); + + SimulateMouseMove(5, 0, 0); + EXPECT_EQ(3U, QueuedEventCount()); + + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + WebGestureEvent::Touchpad); + EXPECT_EQ(4U, QueuedEventCount()); + + SimulateTouchEvent(1, 1); + EXPECT_EQ(5U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, ClientOnSendEventHonored) { + client_->set_allow_send_event(false); + + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateWheelEvent(5, 0, 0, false); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateMouseMove(5, 0, 0); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + WebGestureEvent::Touchpad); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateTouchEvent(1, 1); + EXPECT_EQ(0U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, FlightCountIncrementedOnDeliver) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(0, client_->in_flight_event_count()); + + input_router_->Flush(); + EXPECT_EQ(1, client_->in_flight_event_count()); +} + +TEST_F(BufferedInputRouterTest, FlightCountDecrementedOnAck) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(0, client_->in_flight_event_count()); + + input_router_->Flush(); + EXPECT_EQ(1, client_->in_flight_event_count()); + + // The in-flight count should continue until the flush has finished. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER)); + EXPECT_EQ(1, client_->in_flight_event_count()); + + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(0, client_->in_flight_event_count()); +} + +TEST_F(BufferedInputRouterTest, FilteredEventsNeverQueued) { + // Event should not be queued, but should be ack'ed. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_CONSUMED); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled"); + EXPECT_EQ(0U, QueuedEventCount()); + ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + // Event should not be queued, but should be ack'ed. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled"); + EXPECT_EQ(0U, QueuedEventCount()); + ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + // |INPUT_EVENT_DISPOSITION_UNKNOWN| should drop the event without ack'ing. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_UNKNOWN); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled"); + EXPECT_EQ(0U, QueuedEventCount()); + ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + // Event should be queued. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled"); + EXPECT_EQ(1U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, FollowupEventsInjected) { + // Enable a followup gesture event. + WebGestureEvent followup_event; + followup_event.type = WebInputEvent::GestureScrollBegin; + followup_event.data.scrollUpdate.deltaX = 10; + ack_handler_->set_followup_touch_event(make_scoped_ptr( + new GestureEventWithLatencyInfo(followup_event, ui::LatencyInfo()))); + + // Create an initial packet of { Touch, Key } and start flushing. + SimulateTouchEvent(1, 1); + EXPECT_EQ(1U, QueuedEventCount()); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(2U, QueuedEventCount()); + input_router_->Flush(); + + // Followup only triggered when event handled. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER)); + SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled"); + EXPECT_EQ(2U, QueuedEventCount()); + + // Ack the touch event. + InputEventDispositions dispositions; + dispositions.push_back(INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED); + dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); + ASSERT_TRUE(FinishFlush(dispositions)); + + // Ack'ing the touch event should have inserted the followup gesture event; + // the flush is not complete until the inserted event is ack'ed. + SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled"); + SCOPED_EXPECT(client_->ExpectSendCalled(true), "SendGestureCalled"); + EXPECT_EQ(followup_event.type, client_->sent_gesture_event().event.type); + EXPECT_EQ(2U, QueuedEventCount()); + + // Our packet is now { Gesture, Key }. + InputMsg_HandleEventPacket::Param param; + ASSERT_EQ(1U, process_->sink().message_count()); + ASSERT_TRUE(InputMsg_HandleEventPacket::Read(process_->sink().GetMessageAt(0), + ¶m)); + EventPacket& followup_packet = param.a; + ASSERT_EQ(2U, followup_packet.size()); + ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT, + followup_packet.events()[0]->payload()->GetType()); + ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT, + followup_packet.events()[1]->payload()->GetType()); + const WebInputEventPayload* payload0 = + WebInputEventPayload::Cast(followup_packet.events()[0]->payload()); + const WebInputEventPayload* payload1 = + WebInputEventPayload::Cast(followup_packet.events()[1]->payload()); + EXPECT_EQ(followup_event.type, payload0->web_event()->type); + EXPECT_EQ(WebInputEvent::RawKeyDown, payload1->web_event()->type); + + // Complete the flush; the gesture should have been ack'ed. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + SCOPED_EXPECT(client_->ExpectDidFlushCalled(true), "DidFlushCalled"); + EXPECT_EQ(followup_event.type, ack_handler_->acked_gesture_event().type); + EXPECT_EQ(0U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, FlushRequestedOnQueue) { + // The first queued event should trigger a flush request. + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(1U, QueuedEventCount()); + SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(true), "SetNeedsFlushCalled"); + + // Subsequently queued events will not trigger another flush request. + SimulateWheelEvent(5, 0, 0, false); + EXPECT_EQ(2U, QueuedEventCount()); + SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(false), "SetNeedsFlushCalled"); +} + +TEST_F(BufferedInputRouterTest, GetLastKeyboardEvent) { + EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(WebInputEvent::RawKeyDown, + input_router_->GetLastKeyboardEvent()->type); + + // Queueing another key event does not effect the "last" event. + SimulateKeyboardEvent(WebInputEvent::KeyUp); + EXPECT_EQ(WebInputEvent::RawKeyDown, + input_router_->GetLastKeyboardEvent()->type); + + input_router_->Flush(); + + // Ack'ing the first event should make the second event the "last" event. + InputEventDispositions dispositions; + dispositions.push_back(INPUT_EVENT_IMPL_THREAD_CONSUMED); + dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); + ASSERT_TRUE(FinishFlush(dispositions)); + EXPECT_EQ(WebInputEvent::KeyUp, input_router_->GetLastKeyboardEvent()->type); + + // A key event queued during a flush becomes "last" upon flush completion. + SimulateKeyboardEvent(WebInputEvent::Char); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(WebInputEvent::Char, input_router_->GetLastKeyboardEvent()->type); + + // An empty queue should produce a null "last" event. + input_router_->Flush(); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent()); +} + +TEST_F(BufferedInputRouterTest, UnexpectedAck) { + ASSERT_FALSE(ack_handler_->unexpected_event_ack_called()); + input_router_->OnMessageReceived( + InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions())); + EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); +} + +TEST_F(BufferedInputRouterTest, BadAck) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + input_router_->Flush(); + + ASSERT_FALSE(ack_handler_->unexpected_event_ack_called()); + EventPacket packet; + input_router_->OnMessageReceived( + InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions())); + EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/immediate_input_router.cc b/chromium/content/browser/renderer_host/input/immediate_input_router.cc index 96e8bb9ac44..a4c69054fd1 100644 --- a/chromium/content/browser/renderer_host/input/immediate_input_router.cc +++ b/chromium/content/browser/renderer_host/input/immediate_input_router.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/metrics/histogram.h" #include "content/browser/renderer_host/input/gesture_event_filter.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" #include "content/browser/renderer_host/input/input_router_client.h" #include "content/browser/renderer_host/input/touch_event_queue.h" #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" @@ -20,8 +21,8 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_switches.h" -#include "ui/base/events/event.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/keyboard_codes.h" using base::Time; using base::TimeDelta; @@ -71,12 +72,13 @@ const char* GetEventAckName(InputEventAckState ack_result) { } // namespace -ImmediateInputRouter::ImmediateInputRouter( - RenderProcessHost* process, - InputRouterClient* client, - int routing_id) +ImmediateInputRouter::ImmediateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id) : process_(process), client_(client), + ack_handler_(ack_handler), routing_id_(routing_id), select_range_pending_(false), move_caret_pending_(false), @@ -85,6 +87,9 @@ ImmediateInputRouter::ImmediateInputRouter( has_touch_handler_(false), touch_event_queue_(new TouchEventQueue(this)), gesture_event_filter_(new GestureEventFilter(this)) { + enable_no_touch_to_renderer_while_scrolling_ = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoTouchToRendererWhileScrolling); DCHECK(process); DCHECK(client); } @@ -92,20 +97,23 @@ ImmediateInputRouter::ImmediateInputRouter( ImmediateInputRouter::~ImmediateInputRouter() { } -bool ImmediateInputRouter::SendInput(IPC::Message* message) { +void ImmediateInputRouter::Flush() { + NOTREACHED() << "ImmediateInputRouter will never request a flush."; +} + +bool ImmediateInputRouter::SendInput(scoped_ptr<IPC::Message> message) { DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); - scoped_ptr<IPC::Message> scoped_message(message); - switch (scoped_message->type()) { + switch (message->type()) { // Check for types that require an ACK. case InputMsg_SelectRange::ID: - return SendSelectRange(scoped_message.release()); + return SendSelectRange(message.Pass()); case InputMsg_MoveCaret::ID: - return SendMoveCaret(scoped_message.release()); + return SendMoveCaret(message.Pass()); case InputMsg_HandleInputEvent::ID: NOTREACHED() << "WebInputEvents should never be sent via SendInput."; return false; default: - return Send(scoped_message.release()); + return Send(message.release()); } } @@ -199,6 +207,7 @@ void ImmediateInputRouter::SendKeyboardEvent( void ImmediateInputRouter::SendGestureEvent( const GestureEventWithLatencyInfo& gesture_event) { + HandleGestureScroll(gesture_event); if (!client_->OnSendGestureEvent(gesture_event)) return; FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); @@ -252,6 +261,7 @@ void ImmediateInputRouter::SendTouchEventImmediately( void ImmediateInputRouter::SendGestureEventImmediately( const GestureEventWithLatencyInfo& gesture_event) { + HandleGestureScroll(gesture_event); if (!client_->OnSendGestureEventImmediately(gesture_event)) return; FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); @@ -277,10 +287,6 @@ bool ImmediateInputRouter::ShouldForwardGestureEvent( return gesture_event_filter_->ShouldForward(touch_event); } -bool ImmediateInputRouter::HasQueuedGestureEvents() const { - return gesture_event_filter_->HasQueuedGestureEvents(); -} - bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { bool handled = true; bool message_is_ok = true; @@ -294,7 +300,7 @@ bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { IPC_END_MESSAGE_MAP() if (!message_is_ok) - client_->OnUnexpectedEventAck(true); + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); return handled; } @@ -302,29 +308,29 @@ bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { void ImmediateInputRouter::OnTouchEventAck( const TouchEventWithLatencyInfo& event, InputEventAckState ack_result) { - client_->OnTouchEventAck(event, ack_result); + ack_handler_->OnTouchEventAck(event, ack_result); } -bool ImmediateInputRouter::SendSelectRange(IPC::Message* message) { +bool ImmediateInputRouter::SendSelectRange(scoped_ptr<IPC::Message> message) { DCHECK(message->type() == InputMsg_SelectRange::ID); if (select_range_pending_) { - next_selection_range_.reset(message); + next_selection_range_ = message.Pass(); return true; } select_range_pending_ = true; - return Send(message); + return Send(message.release()); } -bool ImmediateInputRouter::SendMoveCaret(IPC::Message* message) { +bool ImmediateInputRouter::SendMoveCaret(scoped_ptr<IPC::Message> message) { DCHECK(message->type() == InputMsg_MoveCaret::ID); if (move_caret_pending_) { - next_move_caret_.reset(message); + next_move_caret_ = message.Pass(); return true; } move_caret_pending_ = true; - return Send(message); + return Send(message.release()); } bool ImmediateInputRouter::Send(IPC::Message* message) { @@ -398,7 +404,7 @@ void ImmediateInputRouter::FilterAndSendWebInputEvent( // Proceed as normal. case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: break; - }; + } // Transmit any pending wheel events on a non-wheel event. This ensures that // the renderer receives the final PhaseEnded wheel event, which is necessary @@ -434,13 +440,13 @@ void ImmediateInputRouter::OnInputEventAck( void ImmediateInputRouter::OnMsgMoveCaretAck() { move_caret_pending_ = false; if (next_move_caret_) - SendMoveCaret(next_move_caret_.release()); + SendMoveCaret(next_move_caret_.Pass()); } void ImmediateInputRouter::OnSelectRangeAck() { select_range_pending_ = false; if (next_selection_range_) - SendSelectRange(next_selection_range_.release()); + SendSelectRange(next_selection_range_.Pass()); } void ImmediateInputRouter::OnHasTouchEventHandlers(bool has_handlers) { @@ -461,7 +467,7 @@ void ImmediateInputRouter::ProcessInputEventAck( int type = static_cast<int>(event_type); if (type < WebInputEvent::Undefined) { - client_->OnUnexpectedEventAck(true); + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); } else if (type == WebInputEvent::MouseMove) { mouse_move_pending_ = false; @@ -501,23 +507,17 @@ void ImmediateInputRouter::ProcessKeyboardAck( int type, InputEventAckState ack_result) { if (key_queue_.empty()) { - LOG(ERROR) << "Got a KeyEvent back from the renderer but we " - << "don't seem to have sent it to the renderer!"; + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); } 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 char event // suppression so that we can resume from the error. key_queue_.clear(); - client_->OnUnexpectedEventAck(false); + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE); } else { NativeWebKeyboardEvent front_item = key_queue_.front(); key_queue_.pop_front(); - client_->OnKeyboardEventAck(front_item, ack_result); - + ack_handler_->OnKeyboardEventAck(front_item, ack_result); // WARNING: This ImmediateInputRouter can be deallocated at this point // (i.e. in the case of Ctrl+W, where the call to // HandleKeyboardEvent destroys this ImmediateInputRouter). @@ -530,7 +530,7 @@ void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) { // Process the unhandled wheel event here before calling // ForwardWheelEventWithLatencyInfo() since it will mutate // current_wheel_event_. - client_->OnWheelEventAck(current_wheel_event_.event, ack_result); + ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result); // Now send the next (coalesced) mouse wheel event. if (!coalesced_mouse_wheel_events_.empty()) { @@ -544,7 +544,7 @@ void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) { void ImmediateInputRouter::ProcessGestureAck(int type, InputEventAckState ack_result) { const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result); - client_->OnGestureEventAck( + ack_handler_->OnGestureEventAck( gesture_event_filter_->GetGestureEventAwaitingAck(), ack_result); gesture_event_filter_->ProcessGestureAck(processed, type); } @@ -556,4 +556,19 @@ void ImmediateInputRouter::ProcessTouchAck( touch_event_queue_->ProcessTouchAck(ack_result, latency_info); } +void ImmediateInputRouter::HandleGestureScroll( + const GestureEventWithLatencyInfo& gesture_event) { + if (!enable_no_touch_to_renderer_while_scrolling_) + return; + + // Once scrolling is started stop forwarding touch move events to renderer. + if (gesture_event.event.type == WebInputEvent::GestureScrollBegin) + touch_event_queue_->set_no_touch_move_to_renderer(true); + + if (gesture_event.event.type == WebInputEvent::GestureScrollEnd || + gesture_event.event.type == WebInputEvent::GestureFlingStart) { + touch_event_queue_->set_no_touch_move_to_renderer(false); + } +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/input/immediate_input_router.h b/chromium/content/browser/renderer_host/input/immediate_input_router.h index 270ca3dc67e..1d1a0c0e168 100644 --- a/chromium/content/browser/renderer_host/input/immediate_input_router.h +++ b/chromium/content/browser/renderer_host/input/immediate_input_router.h @@ -21,6 +21,7 @@ struct LatencyInfo; namespace content { class GestureEventFilter; +class InputAckHandler; class InputRouterClient; class RenderProcessHost; class RenderWidgetHostImpl; @@ -33,11 +34,13 @@ class CONTENT_EXPORT ImmediateInputRouter public: ImmediateInputRouter(RenderProcessHost* process, InputRouterClient* client, + InputAckHandler* ack_handler, int routing_id); virtual ~ImmediateInputRouter(); // InputRouter - virtual bool SendInput(IPC::Message* message) OVERRIDE; + virtual void Flush() OVERRIDE; + virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE; virtual void SendMouseEvent( const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; virtual void SendWheelEvent( @@ -59,7 +62,6 @@ class CONTENT_EXPORT ImmediateInputRouter virtual bool ShouldForwardTouchEvent() const OVERRIDE; virtual bool ShouldForwardGestureEvent( const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE; - virtual bool HasQueuedGestureEvents() const OVERRIDE; // IPC::Listener virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; @@ -73,12 +75,14 @@ class CONTENT_EXPORT ImmediateInputRouter } private: + friend class ImmediateInputRouterTest; + // TouchEventQueueClient virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, InputEventAckState ack_result) OVERRIDE; - bool SendMoveCaret(IPC::Message* message); - bool SendSelectRange(IPC::Message* message); + bool SendMoveCaret(scoped_ptr<IPC::Message> message); + bool SendSelectRange(scoped_ptr<IPC::Message> message); bool Send(IPC::Message* message); // Transmits the given input event an as an IPC::Message. This is an internal @@ -127,11 +131,15 @@ private: void ProcessTouchAck(InputEventAckState ack_result, const ui::LatencyInfo& latency_info); + void HandleGestureScroll( + const GestureEventWithLatencyInfo& gesture_event); + int routing_id() const { return routing_id_; } RenderProcessHost* process_; InputRouterClient* client_; + InputAckHandler* ack_handler_; int routing_id_; // (Similar to |mouse_move_pending_|.) True while waiting for SelectRange_ACK. @@ -186,6 +194,10 @@ private: // not sent to the renderer. bool has_touch_handler_; + // Whether enabling the optimization that sending no touch move events to + // renderer while scrolling. + bool enable_no_touch_to_renderer_while_scrolling_; + scoped_ptr<TouchEventQueue> touch_event_queue_; scoped_ptr<GestureEventFilter> gesture_event_filter_; diff --git a/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc b/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc index ed725ee1a10..0e4767a3239 100644 --- a/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc +++ b/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc @@ -8,6 +8,8 @@ #include "content/browser/renderer_host/input/gesture_event_filter.h" #include "content/browser/renderer_host/input/immediate_input_router.h" #include "content/browser/renderer_host/input/input_router_client.h" +#include "content/browser/renderer_host/input/input_router_unittest.h" +#include "content/browser/renderer_host/input/mock_input_router_client.h" #include "content/common/content_constants_internal.h" #include "content/common/edit_command.h" #include "content/common/input_messages.h" @@ -15,11 +17,11 @@ #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #if defined(OS_WIN) || defined(USE_AURA) #include "content/browser/renderer_host/ui_events_helper.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" #endif using base::TimeDelta; @@ -100,266 +102,22 @@ bool EventListIsSubset(const ScopedVector<ui::TouchEvent>& subset, } // namespace -class MockInputRouterClient : public InputRouterClient { +class ImmediateInputRouterTest : public InputRouterTest { public: - MockInputRouterClient() - : input_router_(NULL), - in_flight_event_count_(0), - has_touch_handler_(false), - ack_count_(0), - unexpected_event_ack_called_(false), - ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN), - filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED), - is_shortcut_(false), - allow_send_key_event_(true), - send_called_(false), - send_immediately_called_(false) { - } - virtual ~MockInputRouterClient() { - } - - // InputRouterClient - virtual InputEventAckState FilterInputEvent( - const WebInputEvent& input_event, - const ui::LatencyInfo& latency_info) OVERRIDE { - return filter_state_; - } - - // Called each time a WebInputEvent IPC is sent. - virtual void IncrementInFlightEventCount() OVERRIDE { - ++in_flight_event_count_; - } - - // Called each time a WebInputEvent ACK IPC is received. - virtual void DecrementInFlightEventCount() OVERRIDE { - --in_flight_event_count_; - } - - // Called when the renderer notifies that it has touch event handlers. - virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE { - has_touch_handler_ = has_handlers; - } - - virtual bool OnSendKeyboardEvent( - const NativeWebKeyboardEvent& key_event, - const ui::LatencyInfo& latency_info, - bool* is_shortcut) OVERRIDE { - send_called_ = true; - sent_key_event_ = key_event; - *is_shortcut = is_shortcut_; - - return allow_send_key_event_; - } - - virtual bool OnSendWheelEvent( - const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE { - send_called_ = true; - sent_wheel_event_ = wheel_event; - - return true; - } - - virtual bool OnSendMouseEvent( - const MouseEventWithLatencyInfo& mouse_event) OVERRIDE { - send_called_ = true; - sent_mouse_event_ = mouse_event; - - return true; - } - - virtual bool OnSendTouchEvent( - const TouchEventWithLatencyInfo& touch_event) OVERRIDE { - send_called_ = true; - sent_touch_event_ = touch_event; - - return true; - } - - virtual bool OnSendGestureEvent( - const GestureEventWithLatencyInfo& gesture_event) OVERRIDE { - send_called_ = true; - sent_gesture_event_ = gesture_event; - - return input_router_->ShouldForwardGestureEvent(gesture_event); - } - - virtual bool OnSendMouseEventImmediately( - const MouseEventWithLatencyInfo& mouse_event) OVERRIDE { - send_immediately_called_ = true; - immediately_sent_mouse_event_ = mouse_event; - - return true; - } - - virtual bool OnSendTouchEventImmediately( - const TouchEventWithLatencyInfo& touch_event) OVERRIDE { - send_immediately_called_ = true; - immediately_sent_touch_event_ = touch_event; - - return true; - } - - virtual bool OnSendGestureEventImmediately( - const GestureEventWithLatencyInfo& gesture_event) OVERRIDE { - send_immediately_called_ = true; - immediately_sent_gesture_event_ = gesture_event; - - return true; - } - - // Called upon event ack receipt from the renderer. - virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - acked_key_event_ = event; - RecordAckCalled(ack_result); - } - virtual void OnWheelEventAck(const WebMouseWheelEvent& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - acked_wheel_event_ = event; - RecordAckCalled(ack_result); - } - virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - acked_touch_event_ = event; - RecordAckCalled(ack_result); - } - virtual void OnGestureEventAck(const WebGestureEvent& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - RecordAckCalled(ack_result); - } - virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - unexpected_event_ack_called_ = true; - } - - void ExpectSendCalled(bool called) { - EXPECT_EQ(called, send_called_); - send_called_ = false; - } - void ExpectSendImmediatelyCalled(bool called) { - EXPECT_EQ(called, send_immediately_called_); - send_immediately_called_ = false; - } - void ExpectAckCalled(int times) { - EXPECT_EQ(times, ack_count_); - ack_count_ = 0; - } - - void set_input_router(InputRouter* input_router) { - input_router_ = input_router; - } - bool has_touch_handler() const { return has_touch_handler_; } - InputEventAckState ack_state() const { return ack_state_; } - void set_filter_state(InputEventAckState filter_state) { - filter_state_ = filter_state; - } - bool unexpected_event_ack_called() const { - return unexpected_event_ack_called_; - } - const NativeWebKeyboardEvent& acked_keyboard_event() { - return acked_key_event_; - } - const WebMouseWheelEvent& acked_wheel_event() { - return acked_wheel_event_; - } - const TouchEventWithLatencyInfo& acked_touch_event() { - return acked_touch_event_; - } - void set_is_shortcut(bool is_shortcut) { - is_shortcut_ = is_shortcut; - } - void set_allow_send_key_event(bool allow) { - allow_send_key_event_ = allow; - } - const NativeWebKeyboardEvent& sent_key_event() { - return sent_key_event_; - } - const MouseWheelEventWithLatencyInfo& sent_wheel_event() { - return sent_wheel_event_; - } - const MouseEventWithLatencyInfo& sent_mouse_event() { - return sent_mouse_event_; - } - const GestureEventWithLatencyInfo& sent_gesture_event() { - return sent_gesture_event_; - } - const MouseEventWithLatencyInfo& immediately_sent_mouse_event() { - return immediately_sent_mouse_event_; - } - const TouchEventWithLatencyInfo& immediately_sent_touch_event() { - return immediately_sent_touch_event_; - } - const GestureEventWithLatencyInfo& immediately_sent_gesture_event() { - return immediately_sent_gesture_event_; - } - - private: - void RecordAckCalled(InputEventAckState ack_result) { - ++ack_count_; - ack_state_ = ack_result; - } - - InputRouter* input_router_; - int in_flight_event_count_; - bool has_touch_handler_; - - int ack_count_; - bool unexpected_event_ack_called_; - InputEventAckState ack_state_; - InputEventAckState filter_state_; - NativeWebKeyboardEvent acked_key_event_; - WebMouseWheelEvent acked_wheel_event_; - TouchEventWithLatencyInfo acked_touch_event_; - - bool is_shortcut_; - bool allow_send_key_event_; - bool send_called_; - NativeWebKeyboardEvent sent_key_event_; - MouseWheelEventWithLatencyInfo sent_wheel_event_; - MouseEventWithLatencyInfo sent_mouse_event_; - TouchEventWithLatencyInfo sent_touch_event_; - GestureEventWithLatencyInfo sent_gesture_event_; - - bool send_immediately_called_; - MouseEventWithLatencyInfo immediately_sent_mouse_event_; - TouchEventWithLatencyInfo immediately_sent_touch_event_; - GestureEventWithLatencyInfo immediately_sent_gesture_event_; -}; - -class ImmediateInputRouterTest : public testing::Test { - public: - ImmediateInputRouterTest() { - } - virtual ~ImmediateInputRouterTest() { - } + ImmediateInputRouterTest() {} + virtual ~ImmediateInputRouterTest() {} protected: - // testing::Test - virtual void SetUp() { - browser_context_.reset(new TestBrowserContext()); - process_.reset(new MockRenderProcessHost(browser_context_.get())); - client_.reset(new MockInputRouterClient()); - input_router_.reset(new ImmediateInputRouter( - process_.get(), - client_.get(), - MSG_ROUTING_NONE)); - client_->set_input_router(input_router_.get()); - } - virtual void TearDown() { - // Process all pending tasks to avoid leaks. - base::MessageLoop::current()->RunUntilIdle(); - - input_router_.reset(); - client_.reset(); - process_.reset(); - browser_context_.reset(); + // InputRouterTest + virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* handler, + int routing_id) OVERRIDE { + return scoped_ptr<InputRouter>( + new ImmediateInputRouter(process, client, handler, routing_id)); } - void SendInputEventACK(WebInputEvent::Type type, + void SendInputEventACK(WebKit::WebInputEvent::Type type, InputEventAckState ack_result) { scoped_ptr<IPC::Message> response( new InputHostMsg_HandleInputEvent_ACK(0, type, ack_result, @@ -367,177 +125,16 @@ class ImmediateInputRouterTest : public testing::Test { input_router_->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. - input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo()); - client_->ExpectSendCalled(true); - EXPECT_EQ(type, client_->sent_key_event().type); - EXPECT_EQ(key_event.windowsKeyCode, - client_->sent_key_event().windowsKeyCode); - } - - void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) { - WebMouseWheelEvent wheel_event; - wheel_event.type = WebInputEvent::MouseWheel; - wheel_event.deltaX = dX; - wheel_event.deltaY = dY; - wheel_event.modifiers = modifiers; - wheel_event.hasPreciseScrollingDeltas = precise; - input_router_->SendWheelEvent( - MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); - client_->ExpectSendCalled(true); - EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); - EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX); - } - - void SimulateMouseMove(int x, int y, int modifiers) { - WebMouseEvent mouse_event; - mouse_event.type = WebInputEvent::MouseMove; - mouse_event.x = mouse_event.windowX = x; - mouse_event.y = mouse_event.windowY = y; - mouse_event.modifiers = modifiers; - input_router_->SendMouseEvent( - MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo())); - client_->ExpectSendCalled(true); - client_->ExpectSendImmediatelyCalled(true); - EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type); - EXPECT_EQ(x, client_->sent_mouse_event().event.x); - EXPECT_EQ(mouse_event.type, - client_->immediately_sent_mouse_event().event.type); - EXPECT_EQ(x, client_->immediately_sent_mouse_event().event.x); - } - - void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) { - WebMouseWheelEvent wheel_event; - wheel_event.type = WebInputEvent::MouseWheel; - wheel_event.phase = phase; - input_router_->SendWheelEvent( - MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); - client_->ExpectSendCalled(true); - EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); - EXPECT_EQ(phase, client_->sent_wheel_event().event.phase); - } - - // Inject provided synthetic WebGestureEvent instance. - void SimulateGestureEventCore(WebInputEvent::Type type, - WebGestureEvent::SourceDevice sourceDevice, - WebGestureEvent* gesture_event) { - gesture_event->type = type; - gesture_event->sourceDevice = sourceDevice; - GestureEventWithLatencyInfo gesture_with_latency( - *gesture_event, ui::LatencyInfo()); - input_router_->SendGestureEvent(gesture_with_latency); - client_->ExpectSendCalled(true); - EXPECT_EQ(type, client_->sent_gesture_event().event.type); - EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice); - } - - // Inject simple synthetic WebGestureEvent instances. - void SimulateGestureEvent(WebInputEvent::Type type, - WebGestureEvent::SourceDevice sourceDevice) { - WebGestureEvent gesture_event; - SimulateGestureEventCore(type, sourceDevice, &gesture_event); - } - - void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) { - WebGestureEvent gesture_event; - gesture_event.data.scrollUpdate.deltaX = dX; - gesture_event.data.scrollUpdate.deltaY = dY; - gesture_event.modifiers = modifiers; - SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate, - WebGestureEvent::Touchscreen, &gesture_event); - } - - void SimulateGesturePinchUpdateEvent(float scale, - float anchorX, - float anchorY, - int modifiers) { - WebGestureEvent gesture_event; - gesture_event.data.pinchUpdate.scale = scale; - gesture_event.x = anchorX; - gesture_event.y = anchorY; - gesture_event.modifiers = modifiers; - SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate, - WebGestureEvent::Touchscreen, &gesture_event); - } - - // Inject synthetic GestureFlingStart events. - void SimulateGestureFlingStartEvent( - float velocityX, - float velocityY, - WebGestureEvent::SourceDevice sourceDevice) { - WebGestureEvent gesture_event; - gesture_event.data.flingStart.velocityX = velocityX; - gesture_event.data.flingStart.velocityY = velocityY; - SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice, - &gesture_event); - } - - // Set the timestamp for the touch-event. - void SetTouchTimestamp(base::TimeDelta timestamp) { - touch_event_.timeStampSeconds = timestamp.InSecondsF(); - } - - // Sends a touch event (irrespective of whether the page has a touch-event - // handler or not). - void SendTouchEvent() { - input_router_->SendTouchEvent( - TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo())); - - // Mark all the points as stationary. And remove the points that have been - // released. - int point = 0; - for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) { - if (touch_event_.touches[i].state == WebTouchPoint::StateReleased) - continue; - - touch_event_.touches[point] = touch_event_.touches[i]; - touch_event_.touches[point].state = - WebTouchPoint::StateStationary; - ++point; - } - touch_event_.touchesLength = point; - touch_event_.type = WebInputEvent::Undefined; - } - - int PressTouchPoint(int x, int y) { - if (touch_event_.touchesLength == touch_event_.touchesLengthCap) - return -1; - WebTouchPoint& point = - touch_event_.touches[touch_event_.touchesLength]; - point.id = touch_event_.touchesLength; - point.position.x = point.screenPosition.x = x; - point.position.y = point.screenPosition.y = y; - point.state = WebTouchPoint::StatePressed; - point.radiusX = point.radiusY = 1.f; - ++touch_event_.touchesLength; - touch_event_.type = WebInputEvent::TouchStart; - return point.id; - } - - void MoveTouchPoint(int index, int x, int y) { - CHECK(index >= 0 && index < touch_event_.touchesLengthCap); - WebTouchPoint& point = touch_event_.touches[index]; - point.position.x = point.screenPosition.x = x; - point.position.y = point.screenPosition.y = y; - touch_event_.touches[index].state = WebTouchPoint::StateMoved; - touch_event_.type = WebInputEvent::TouchMove; - } - - void ReleaseTouchPoint(int index) { - CHECK(index >= 0 && index < touch_event_.touchesLengthCap); - touch_event_.touches[index].state = WebTouchPoint::StateReleased; - touch_event_.type = WebInputEvent::TouchEnd; + ImmediateInputRouter* input_router() const { + return static_cast<ImmediateInputRouter*>(input_router_.get()); } void set_debounce_interval_time_ms(int ms) { - input_router_->gesture_event_filter()->debounce_interval_time_ms_ = ms; + input_router()->gesture_event_filter()->debounce_interval_time_ms_ = ms; } void set_maximum_tap_gap_time_ms(int delay_ms) { - input_router_->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms; + input_router()->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms; } size_t TouchEventQueueSize() { @@ -548,8 +145,16 @@ class ImmediateInputRouterTest : public testing::Test { return touch_event_queue()->GetLatestEvent().event; } + void EnableNoTouchToRendererWhileScrolling() { + input_router()->enable_no_touch_to_renderer_while_scrolling_ = true; + } + + bool no_touch_move_to_renderer() { + return touch_event_queue()->no_touch_move_to_renderer_; + } + TouchEventQueue* touch_event_queue() const { - return input_router_->touch_event_queue(); + return input_router()->touch_event_queue(); } unsigned GestureEventLastQueueEventSize() { @@ -590,18 +195,8 @@ class ImmediateInputRouterTest : public testing::Test { } GestureEventFilter* gesture_event_filter() const { - return input_router_->gesture_event_filter(); + return input_router()->gesture_event_filter(); } - - scoped_ptr<MockRenderProcessHost> process_; - scoped_ptr<MockInputRouterClient> client_; - scoped_ptr<ImmediateInputRouter> input_router_; - - private: - base::MessageLoopForUI message_loop_; - WebTouchEvent touch_event_; - - scoped_ptr<TestBrowserContext> browser_context_; }; #if GTEST_HAS_PARAM_TEST @@ -613,8 +208,8 @@ class ImmediateInputRouterWithSourceTest #endif // GTEST_HAS_PARAM_TEST TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) { - input_router_->SendInput( - new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4))); + input_router_->SendInput(scoped_ptr<IPC::Message>( + new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)))); EXPECT_EQ(1u, process_->sink().message_count()); ExpectIPCMessageWithArg2<InputMsg_SelectRange>( process_->sink().GetMessageAt(0), @@ -623,12 +218,12 @@ TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) { process_->sink().ClearMessages(); // Send two more messages without acking. - input_router_->SendInput( - new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8))); + input_router_->SendInput(scoped_ptr<IPC::Message>( + new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8)))); EXPECT_EQ(0u, process_->sink().message_count()); - input_router_->SendInput( - new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12))); + input_router_->SendInput(scoped_ptr<IPC::Message>( + new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12)))); EXPECT_EQ(0u, process_->sink().message_count()); // Now ack the first message. @@ -654,17 +249,20 @@ TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) { } TEST_F(ImmediateInputRouterTest, CoalescesCaretMove) { - input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(1, 2))); + input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(1, 2)))); EXPECT_EQ(1u, process_->sink().message_count()); ExpectIPCMessageWithArg1<InputMsg_MoveCaret>( process_->sink().GetMessageAt(0), gfx::Point(1, 2)); process_->sink().ClearMessages(); // Send two more messages without acking. - input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(5, 6))); + input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(5, 6)))); EXPECT_EQ(0u, process_->sink().message_count()); - input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(9, 10))); + input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(9, 10)))); EXPECT_EQ(0u, process_->sink().message_count()); // Now ack the first message. @@ -697,7 +295,7 @@ TEST_F(ImmediateInputRouterTest, HandledInputEvent) { EXPECT_EQ(0u, process_->sink().message_count()); // OnKeyboardEventAck should be triggered without actual ack. - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); // As the event was acked already, keyboard event queue should be // empty. @@ -705,14 +303,14 @@ TEST_F(ImmediateInputRouterTest, HandledInputEvent) { } TEST_F(ImmediateInputRouterTest, ClientCanceledKeyboardEvent) { - client_->set_allow_send_key_event(false); + client_->set_allow_send_event(false); // Simulate a keyboard event. SimulateKeyboardEvent(WebInputEvent::RawKeyDown); // Make sure no input event is sent to the renderer. EXPECT_EQ(0u, process_->sink().message_count()); - client_->ExpectAckCalled(0); + ack_handler_->ExpectAckCalled(0); } TEST_F(ImmediateInputRouterTest, ShortcutKeyboardEvent) { @@ -734,7 +332,7 @@ TEST_F(ImmediateInputRouterTest, NoncorrespondingKeyEvents) { SendInputEventACK(WebInputEvent::KeyUp, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_TRUE(client_->unexpected_event_ack_called()); + EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); } // Tests ported from RenderWidgetHostTest -------------------------------------- @@ -754,8 +352,9 @@ TEST_F(ImmediateInputRouterTest, HandleKeyEventsWeSent) { // Send the simulated response from the renderer back. SendInputEventACK(WebInputEvent::RawKeyDown, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - client_->ExpectAckCalled(1); - EXPECT_EQ(WebInputEvent::RawKeyDown, client_->acked_keyboard_event().type); + ack_handler_->ExpectAckCalled(1); + EXPECT_EQ(WebInputEvent::RawKeyDown, + ack_handler_->acked_keyboard_event().type); } TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) { @@ -763,7 +362,7 @@ TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) { SendInputEventACK(WebInputEvent::RawKeyDown, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - client_->ExpectAckCalled(0); + ack_handler_->ExpectAckCalled(0); } TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { @@ -786,7 +385,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { // so that additional input events can be processed before // we turn off coalescing. base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -796,7 +395,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -806,7 +405,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, process_->sink().message_count()); // FIXME(kouhei): Below is testing gesture event filter. Maybe separate test? @@ -909,7 +508,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollBegin, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -919,7 +518,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -929,7 +528,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -939,7 +538,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollEnd, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, process_->sink().message_count()); } @@ -1192,14 +791,14 @@ TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) { SendInputEventACK(WebInputEvent::GestureFlingStart, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device); EXPECT_FALSE(FlingInProgress()); EXPECT_EQ(2U, process_->sink().message_count()); SendInputEventACK(WebInputEvent::GestureFlingCancel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, GestureEventLastQueueEventSize()); // GFC before previous GFS is acked. @@ -1218,7 +817,7 @@ TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) { SendInputEventACK(WebInputEvent::GestureFlingCancel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(2); + ack_handler_->ExpectAckCalled(2); EXPECT_EQ(0U, GestureEventLastQueueEventSize()); // GFS is added to the queue if another event is pending @@ -1563,18 +1162,18 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueue) { SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, TouchEventQueueSize()); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(WebInputEvent::TouchStart, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, TouchEventQueueSize()); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); EXPECT_EQ(0U, process_->sink().message_count()); } @@ -1615,8 +1214,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueFlush) { INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(31U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchStart, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); @@ -1663,8 +1262,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) { EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_EQ(2U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchStart, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); process_->sink().ClearMessages(); // Coalesced touch-move events should be sent. @@ -1678,8 +1277,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) { EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_EQ(1U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(10); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(10); process_->sink().ClearMessages(); // ACK the release. @@ -1688,8 +1287,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) { EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(0U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchEnd, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); } // Tests that an event that has already been sent but hasn't been ack'ed yet @@ -1844,8 +1443,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); EXPECT_EQ(0U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(2); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(2); EXPECT_EQ(0U, process_->sink().message_count()); process_->sink().ClearMessages(); @@ -1854,8 +1453,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { SendTouchEvent(); EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(WebInputEvent::TouchEnd, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); // Send a press-event, followed by move and release events, and another press // event, before the ACK for the first press event comes back. All of the @@ -1883,8 +1482,9 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); EXPECT_EQ(1U, process_->sink().message_count()); - EXPECT_EQ(WebInputEvent::TouchEnd, client_->acked_touch_event().event.type); - client_->ExpectAckCalled(4); + EXPECT_EQ(WebInputEvent::TouchEnd, + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(4); EXPECT_EQ(1U, TouchEventQueueSize()); process_->sink().ClearMessages(); @@ -1892,8 +1492,9 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); EXPECT_EQ(0U, process_->sink().message_count()); - EXPECT_EQ(WebInputEvent::TouchStart, client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + EXPECT_EQ(WebInputEvent::TouchStart, + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, TouchEventQueueSize()); // Send a second press event. Even though the first touch had NO_CONSUMER, @@ -2030,7 +1631,7 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) { EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_EQ(2U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); process_->sink().ClearMessages(); // ACK the press with NO_CONSUMED_EXISTS. This should release the queued @@ -2040,7 +1641,7 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) { EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(0U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); ReleaseTouchPoint(2); ReleaseTouchPoint(1); @@ -2118,11 +1719,11 @@ TEST_F(ImmediateInputRouterTest, AckedTouchEventState) { for (size_t i = 0; i < arraysize(acks); ++i) { SendInputEventACK(acks[i], INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(acks[i], client_->acked_touch_event().event.type); + EXPECT_EQ(acks[i], ack_handler_->acked_touch_event().event.type); ScopedVector<ui::TouchEvent> acked; MakeUITouchEventsFromWebTouchEvents( - client_->acked_touch_event(), &acked, coordinate_system); + ack_handler_->acked_touch_event(), &acked, coordinate_system); bool success = EventListIsSubset(acked, expected_events); EXPECT_TRUE(success) << "Failed on step: " << i; if (!success) @@ -2151,9 +1752,9 @@ TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) { INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Check that the correct unhandled wheel event was received. - client_->ExpectAckCalled(1); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, client_->ack_state()); - EXPECT_EQ(client_->acked_wheel_event().deltaY, -5); + ack_handler_->ExpectAckCalled(1); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state()); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); // Check that the second event was sent. EXPECT_EQ(1U, process_->sink().message_count()); @@ -2162,7 +1763,96 @@ TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) { process_->sink().ClearMessages(); // Check that the correct unhandled wheel event was received. - EXPECT_EQ(client_->acked_wheel_event().deltaY, -5); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); } +// Tests that no touch move events are sent to renderer during scrolling. +TEST_F(ImmediateInputRouterTest, NoTouchMoveWhileScroll) { + EnableNoTouchToRendererWhileScrolling(); + set_debounce_interval_time_ms(0); + input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); + process_->sink().ClearMessages(); + + // First touch press. + PressTouchPoint(0, 1); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchStart, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Touch move will trigger scroll. + MoveTouchPoint(0, 20, 5); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + WebGestureEvent::Touchscreen); + EXPECT_EQ(1U, process_->sink().message_count()); + EXPECT_TRUE(no_touch_move_to_renderer()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::GestureScrollBegin, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Touch move should not be sent to renderer. + MoveTouchPoint(0, 30, 5); + SendTouchEvent(); + EXPECT_EQ(0U, process_->sink().message_count()); + process_->sink().ClearMessages(); + + // Touch moves become ScrollUpdate. + SimulateGestureScrollUpdateEvent(20, 4, 0); + EXPECT_TRUE(no_touch_move_to_renderer()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Touch move should not be sent to renderer. + MoveTouchPoint(0, 65, 10); + SendTouchEvent(); + EXPECT_EQ(0U, process_->sink().message_count()); + process_->sink().ClearMessages(); + + // Touch end should still be sent to renderer. + ReleaseTouchPoint(0); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchEnd, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // On GestureScrollEnd, resume sending touch moves to renderer. + SimulateGestureEvent(WebKit::WebInputEvent::GestureScrollEnd, + WebGestureEvent::Touchscreen); + EXPECT_EQ(1U, process_->sink().message_count()); + EXPECT_FALSE(no_touch_move_to_renderer()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::GestureScrollEnd, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Now touch events should come through to renderer. + PressTouchPoint(80, 10); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchStart, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + MoveTouchPoint(0, 80, 20); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + ReleaseTouchPoint(0); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchEnd, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); +} } // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_ack_handler.h b/chromium/content/browser/renderer_host/input/input_ack_handler.h new file mode 100644 index 00000000000..3e09b07cd03 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_ack_handler.h @@ -0,0 +1,41 @@ +// Copyright 2013 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_INPUT_INPUT_ACK_HANDLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_ + +#include "base/basictypes.h" +#include "content/port/browser/event_with_latency_info.h" +#include "content/port/common/input_event_ack_state.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +namespace content { + +// Provided customized ack response for input events. +class CONTENT_EXPORT InputAckHandler { + public: + virtual ~InputAckHandler() {} + + // Called upon event ack receipt from the renderer. + virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, + InputEventAckState ack_result) = 0; + virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, + InputEventAckState ack_result) = 0; + virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, + InputEventAckState ack_result) = 0; + virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, + InputEventAckState ack_result) = 0; + + enum UnexpectedEventAckType { + UNEXPECTED_ACK, + UNEXPECTED_EVENT_TYPE, + BAD_ACK_MESSAGE + }; + virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_ diff --git a/chromium/content/browser/renderer_host/input/input_queue.cc b/chromium/content/browser/renderer_host/input/input_queue.cc new file mode 100644 index 00000000000..c10500b9c08 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue.cc @@ -0,0 +1,183 @@ +// Copyright 2013 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/input/input_queue.h" + +#include "base/bind.h" +#include "base/debug/trace_event.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_queue_client.h" +#include "content/common/input/event_packet.h" +#include "content/common/input/input_event.h" + +namespace content { + +// A specialized EventPacket with utility methods for dispatched event handling. +class InputQueue::BrowserEventPacket : public EventPacket { + public: + typedef ScopedVector<BrowserInputEvent> BrowserInputEvents; + + BrowserEventPacket() : weak_factory_(this) {} + virtual ~BrowserEventPacket() {} + + // Validate the response and signal dispatch to the processed events. + // Undelivered events will be re-enqueued, and any generated followup events + // will be inserted at the same relative order as their generating event. + AckResult ValidateAndDispatchAck(int64 packet_id, + const InputEventDispositions& dispositions) { + if (!Validate(packet_id, dispositions)) + return ACK_INVALID; + + // Empty the packet; events will be re-enqueued as necessary. + InputEvents dispatched_events; + events_.swap(dispatched_events); + + // The packet could be deleted as a result of event dispatch; use a local + // weak ref to ensure proper shutdown. + base::WeakPtr<EventPacket> weak_ref_this = weak_factory_.GetWeakPtr(); + + BrowserInputEvents followup_events; + for (size_t i = 0; i < dispatched_events.size(); ++i) { + // Take ownership of the event. + scoped_ptr<BrowserInputEvent> event( + static_cast<BrowserInputEvent*>(dispatched_events[i])); + dispatched_events[i] = NULL; + + // Re-enqueue undelivered events. + InputEventDisposition disposition = dispositions[i]; + if (disposition == INPUT_EVENT_COULD_NOT_DELIVER) { + Add(event.PassAs<InputEvent>()); + continue; + } + + event->OnDispatched(disposition, &followup_events); + + // TODO(jdduke): http://crbug.com/274029 + if (!weak_ref_this.get()) + return ACK_SHUTDOWN; + + AddAll(&followup_events); + } + return ACK_OK; + } + + protected: + // Add and take ownership of events in |followup_events|. + void AddAll(BrowserInputEvents* followup_events) { + for (BrowserInputEvents::iterator iter = followup_events->begin(); + iter != followup_events->end(); + ++iter) { + Add(scoped_ptr<InputEvent>(*iter)); + } + followup_events->weak_clear(); + } + + // Perform a sanity check of the ack against the current packet. + // |packet_id| should match that of this packet, and |dispositions| should + // be of size equal to the number of events in this packet. + bool Validate(int64 packet_id, + const InputEventDispositions& dispositions) const { + if (packet_id != id()) + return false; + + if (dispositions.size() != size()) + return false; + + return true; + } + + private: + base::WeakPtrFactory<EventPacket> weak_factory_; +}; + +InputQueue::InputQueue(InputQueueClient* client) + : client_(client), + next_packet_id_(1), + flush_requested_(false), + in_flush_packet_(new BrowserEventPacket()), + pending_flush_packet_(new BrowserEventPacket()) { + DCHECK(client_); +} + +InputQueue::~InputQueue() {} + +void InputQueue::QueueEvent(scoped_ptr<BrowserInputEvent> event) { + DCHECK(event); + DCHECK(event->valid()); + pending_flush_packet_->Add(event.PassAs<InputEvent>()); + RequestFlushIfNecessary(); +} + +void InputQueue::BeginFlush() { + // Ignore repeated flush attempts. + if (!flush_requested_) + return; + + DCHECK(!FlushInProgress()); + DCHECK(!pending_flush_packet_->empty()); + + flush_requested_ = false; + in_flush_packet_.swap(pending_flush_packet_); + DeliverInFlushPacket(); +} + +InputQueue::AckResult InputQueue::OnEventPacketAck( + int64 packet_id, + const InputEventDispositions& dispositions) { + if (!FlushInProgress()) + return ACK_UNEXPECTED; + + TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "AckPacket", + "id", packet_id); + + AckResult ack_result = + in_flush_packet_->ValidateAndDispatchAck(packet_id, dispositions); + + if (ack_result != ACK_OK) + return ack_result; + + if (FlushInProgress()) { + DeliverInFlushPacket(); + } else { + TRACE_EVENT_ASYNC_END0("input", "InputQueueFlush", this); + client_->DidFinishFlush(); + RequestFlushIfNecessary(); + } + + return ACK_OK; +} + +size_t InputQueue::QueuedEventCount() const { + return in_flush_packet_->size() + pending_flush_packet_->size(); +} + +void InputQueue::DeliverInFlushPacket() { + DCHECK(FlushInProgress()); + TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "DeliverPacket", + "id", next_packet_id_); + in_flush_packet_->set_id(next_packet_id_++); + client_->Deliver(*in_flush_packet_); +} + +void InputQueue::RequestFlushIfNecessary() { + if (flush_requested_) + return; + + // Defer flush requests until the current flush has finished. + if (FlushInProgress()) + return; + + // No additional events to flush. + if (pending_flush_packet_->empty()) + return; + + TRACE_EVENT_ASYNC_BEGIN0("input", "InputQueueFlush", this); + TRACE_EVENT_ASYNC_STEP0("input", "InputQueueFlush", this, "Request"); + flush_requested_ = true; + client_->SetNeedsFlush(); +} + +bool InputQueue::FlushInProgress() const { return !in_flush_packet_->empty(); } + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_queue.h b/chromium/content/browser/renderer_host/input/input_queue.h new file mode 100644 index 00000000000..2c7acb707d2 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue.h @@ -0,0 +1,82 @@ +// Copyright 2013 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_INPUT_INPUT_QUEUE_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/common/input/input_event_disposition.h" + +namespace content { + +class BrowserInputEvent; +class EventPacketAck; +class InputQueueClient; + +// |InputQueue| handles browser input event batching and response. +// Event packets are assembled into sequential event packets. A flush entails +// delivery and dispatch of a single event packet, and continues until the +// packet is ack'ed and all its events have been dispatched to the renderer. +class CONTENT_EXPORT InputQueue { + public: + // The |client| should outlive the InputQueue. + InputQueue(InputQueueClient* client); + virtual ~InputQueue(); + + // If a flush is in progress, |event| will be dispatched in the next flush. + // If no flush is in progress, a flush will be requested if necessary. + // |event| is assumed to be both non-NULL and valid. + void QueueEvent(scoped_ptr<BrowserInputEvent> event); + + // Initiates the flush of the pending event packet to |client_|, if necessary. + // This should only be called in response to |client_->SetNeedsFlush()|. + void BeginFlush(); + + // Called by the owner upon EventPacket responses from the sender target. This + // will dispatch event acks for events with a corresponding |ack_handler|. + enum AckResult { + ACK_OK, // |acked_packet| was processed successfully. + ACK_UNEXPECTED, // |acked_packet| was unexpected; no flush was in-progress. + ACK_INVALID, // |acked_packet| contains invalid data. + ACK_SHUTDOWN // |acked_packet| processing triggered queue shutdown. + }; + AckResult OnEventPacketAck(int64 packet_id, + const InputEventDispositions& dispositions); + + // Total number of evenst in the queue, both being flushed and pending flush. + size_t QueuedEventCount() const; + + protected: + friend class InputQueueTest; + + // Delivers |in_flush_packet_| to the client. + void DeliverInFlushPacket(); + + // Requests a flush via |client_| if the necessary request has not been made. + void RequestFlushIfNecessary(); + + // True when |in_flush_packet_| is non-empty. + bool FlushInProgress() const; + + private: + InputQueueClient* client_; + + // Used to assign unique, non-zero ID's to each delivered EventPacket. + int64 next_packet_id_; + + // Avoid spamming the client with redundant flush requests. + bool flush_requested_; + + class BrowserEventPacket; + scoped_ptr<BrowserEventPacket> in_flush_packet_; + scoped_ptr<BrowserEventPacket> pending_flush_packet_; + + DISALLOW_COPY_AND_ASSIGN(InputQueue); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_ diff --git a/chromium/content/browser/renderer_host/input/input_queue_client.h b/chromium/content/browser/renderer_host/input/input_queue_client.h new file mode 100644 index 00000000000..f1151d92fed --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue_client.h @@ -0,0 +1,35 @@ +// Copyright 2013 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_INPUT_INPUT_QUEUE_CLIENT_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_vector.h" +#include "content/common/input/input_event_disposition.h" + +namespace content { + +class EventPacket; + +class InputQueueClient { + public: + virtual ~InputQueueClient() {} + + // Used by the flush process for sending batched events to the desired target. + virtual void Deliver(const EventPacket& packet) = 0; + + // Signalled when the InputQueue has received and dispatched + // event ACK's from the previous flush. + virtual void DidFinishFlush() = 0; + + // Signalled when the InputQueue receives an input event. + virtual void SetNeedsFlush() = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_ diff --git a/chromium/content/browser/renderer_host/input/input_queue_unittest.cc b/chromium/content/browser/renderer_host/input/input_queue_unittest.cc new file mode 100644 index 00000000000..2d183d96cca --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue_unittest.cc @@ -0,0 +1,363 @@ +// Copyright 2013 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/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_queue.h" +#include "content/browser/renderer_host/input/input_queue_client.h" +#include "content/common/input/event_packet.h" +#include "content/common/input/input_event.h" +#include "content/common/input/ipc_input_event_payload.h" +#include "content/common/input/web_input_event_payload.h" +#include "content/common/input_messages.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/latency_info.h" + +namespace content { +namespace { + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; + +class InputQueueTest : public testing::Test, + public InputQueueClient, + public BrowserInputEventClient { + public: + InputQueueTest() + : queue_(new InputQueue(this)), + routing_id_(0), + num_flush_completions_(0), + num_flush_requests_(0), + num_packet_deliveries_(0), + next_input_id_(1) {} + + // InputQueueClient + virtual void Deliver(const EventPacket& packet) OVERRIDE { + EXPECT_LT(0u, packet.size()); + ++num_packet_deliveries_; + current_packet_id_ = packet.id(); + current_packet_dispositions_.resize(packet.size(), INPUT_EVENT_UNHANDLED); + } + + virtual void DidFinishFlush() OVERRIDE { ++num_flush_completions_; } + virtual void SetNeedsFlush() OVERRIDE { ++num_flush_requests_; } + + // BrowserInputEventClient + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) OVERRIDE { + acked_dispositions_.push_back(disposition); + } + + virtual void OnDispatched( + const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) OVERRIDE { + acked_followup_dispositions_.push_back(disposition); + if (event_to_inject_) + followup->push_back(event_to_inject_.release()); + } + + int num_flush_requests() const { return num_flush_requests_; } + int num_flush_completions() const { return num_flush_completions_; } + int num_packet_deliveries() const { return num_packet_deliveries_; } + + protected: + scoped_ptr<BrowserInputEvent> CreateIPCInputEvent(IPC::Message* message) { + return BrowserInputEvent::Create( + NextInputID(), + IPCInputEventPayload::Create(make_scoped_ptr(message)), + this); + } + + scoped_ptr<BrowserInputEvent> CreateWebInputEvent( + WebInputEvent::Type web_type) { + WebKit::WebMouseEvent mouse; + WebKit::WebMouseWheelEvent wheel; + WebKit::WebTouchEvent touch; + WebKit::WebGestureEvent gesture; + WebKit::WebKeyboardEvent keyboard; + + WebKit::WebInputEvent* web_event = NULL; + if (WebInputEvent::isMouseEventType(web_type)) + web_event = &mouse; + else if (WebInputEvent::isKeyboardEventType(web_type)) + web_event = &keyboard; + else if (WebInputEvent::isTouchEventType(web_type)) + web_event = &touch; + else if (WebInputEvent::isGestureEventType(web_type)) + web_event = &gesture; + else + web_event = &wheel; + web_event->type = web_type; + + return BrowserInputEvent::Create( + NextInputID(), + WebInputEventPayload::Create(*web_event, ui::LatencyInfo(), false), + this); + } + + void QueueEvent(IPC::Message* message) { + queue_->QueueEvent(CreateIPCInputEvent(message)); + } + + void QueueEvent(WebInputEvent::Type web_type) { + queue_->QueueEvent(CreateWebInputEvent(web_type)); + } + + bool Flush(InputEventDisposition disposition) { + StartFlush(); + return FinishFlush(disposition); + } + + void StartFlush() { + acked_dispositions_.clear(); + acked_followup_dispositions_.clear(); + current_packet_id_ = 0; + current_packet_dispositions_.clear(); + queue_->BeginFlush(); + } + + bool FinishFlush(InputEventDisposition disposition) { + if (!current_packet_id_) + return false; + current_packet_dispositions_ = InputEventDispositions( + current_packet_dispositions_.size(), disposition); + return InputQueue::ACK_OK == + queue_->OnEventPacketAck(current_packet_id_, + current_packet_dispositions_); + } + + int64 NextInputID() { return next_input_id_++; } + + scoped_ptr<InputQueue> queue_; + + int routing_id_; + int64 current_packet_id_; + InputEventDispositions current_packet_dispositions_; + + InputEventDispositions acked_dispositions_; + InputEventDispositions acked_followup_dispositions_; + scoped_ptr<BrowserInputEvent> event_to_inject_; + + int num_flush_completions_; + int num_flush_requests_; + int num_packet_deliveries_; + int next_input_id_; +}; + +TEST_F(InputQueueTest, SetNeedsFlushOnQueueEvent) { + EXPECT_EQ(0, num_flush_requests()); + + QueueEvent(WebInputEvent::MouseDown); + EXPECT_EQ(1, num_flush_requests()); + + // Additional queued events should not trigger additional flush requests. + QueueEvent(WebInputEvent::MouseUp); + EXPECT_EQ(1, num_flush_requests()); + QueueEvent(WebInputEvent::TouchStart); + EXPECT_EQ(1, num_flush_requests()); +} + +TEST_F(InputQueueTest, NoSetNeedsFlushOnQueueIfFlushing) { + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + + StartFlush(); + EXPECT_EQ(1, num_flush_requests()); + EXPECT_EQ(1, num_packet_deliveries()); + + // Events queued after a flush will not trigger an additional flush request. + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + QueueEvent(WebInputEvent::GestureScrollEnd); + EXPECT_EQ(1, num_flush_requests()); +} + +TEST_F(InputQueueTest, SetNeedsFlushAfterDidFinishFlushIfEventsQueued) { + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + + StartFlush(); + EXPECT_EQ(1, num_packet_deliveries()); + + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + + // An additional flush request is sent for the event queued after the flush. + ASSERT_TRUE(current_packet_id_); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(1, num_flush_completions()); + EXPECT_EQ(2, num_flush_requests()); +} + +TEST_F(InputQueueTest, EventPacketSentAfterFlush) { + EXPECT_EQ(0, num_packet_deliveries()); + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(0, num_packet_deliveries()); + StartFlush(); + EXPECT_EQ(1, num_packet_deliveries()); +} + +TEST_F(InputQueueTest, AcksHandledInProperOrder) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::GestureScrollEnd); + QueueEvent(WebInputEvent::GestureFlingStart); + + queue_->BeginFlush(); + ASSERT_EQ(3u, current_packet_dispositions_.size()); + current_packet_dispositions_[0] = INPUT_EVENT_IMPL_THREAD_CONSUMED; + current_packet_dispositions_[1] = INPUT_EVENT_MAIN_THREAD_CONSUMED; + current_packet_dispositions_[2] = INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS; + queue_->OnEventPacketAck(current_packet_id_, current_packet_dispositions_); + EXPECT_EQ(1, num_flush_completions()); + + ASSERT_EQ(3u, acked_dispositions_.size()); + EXPECT_EQ(acked_dispositions_[0], INPUT_EVENT_IMPL_THREAD_CONSUMED); + EXPECT_EQ(acked_dispositions_[1], INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(acked_dispositions_[2], INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS); +} + +TEST_F(InputQueueTest, FollowupWhenFollowupEventNotConsumed) { + InputEventDisposition unconsumed_dispositions[] = { + INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS, + INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED, + INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS + }; + for (size_t i = 0; i < arraysize(unconsumed_dispositions); ++i) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchStart); + QueueEvent(WebInputEvent::TouchMove); + + Flush(unconsumed_dispositions[i]); + EXPECT_EQ(1u, acked_dispositions_.size()) << i; + EXPECT_EQ(2u, acked_followup_dispositions_.size()) << i; + } +} + +TEST_F(InputQueueTest, NoFollowupWhenFollowupEventConsumed) { + InputEventDisposition consumed_dispositions[] = { + INPUT_EVENT_IMPL_THREAD_CONSUMED, + INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED, + INPUT_EVENT_MAIN_THREAD_CONSUMED + }; + for (size_t i = 0; i < arraysize(consumed_dispositions); ++i) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchStart); + QueueEvent(WebInputEvent::TouchMove); + + Flush(consumed_dispositions[i]); + EXPECT_EQ(3u, acked_dispositions_.size()) << i; + EXPECT_EQ(0u, acked_followup_dispositions_.size()) << i; + } +} + +TEST_F(InputQueueTest, FlushOnEmptyQueueIgnored) { + Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(0, num_flush_requests()); + EXPECT_EQ(0, num_flush_completions()); + + QueueEvent(WebInputEvent::GestureScrollBegin); + Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_requests()); + EXPECT_EQ(1, num_flush_completions()); + + Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_requests()); + EXPECT_EQ(1, num_flush_completions()); +} + +TEST_F(InputQueueTest, FlushContinuesUntilAllEventsProcessed) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::GestureScrollEnd); + QueueEvent(WebInputEvent::GestureFlingStart); + + EXPECT_EQ(1, num_flush_requests()); + Flush(INPUT_EVENT_COULD_NOT_DELIVER); + EXPECT_EQ(0, num_flush_completions()); + + FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER); + EXPECT_EQ(0, num_flush_completions()); + + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED)); + EXPECT_EQ(1, num_flush_completions()); +} + +TEST_F(InputQueueTest, InvalidPacketAckIgnored) { + // Packet never flushed, any ack should be ignored. + InputQueue::AckResult result = + queue_->OnEventPacketAck(0, InputEventDispositions()); + EXPECT_EQ(InputQueue::ACK_UNEXPECTED, result); + + QueueEvent(WebInputEvent::GestureScrollBegin); + StartFlush(); + // Tamper with the sent packet by adding an extra event. + current_packet_dispositions_.push_back(INPUT_EVENT_MAIN_THREAD_CONSUMED); + bool valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(0, num_flush_completions()); + EXPECT_FALSE(valid_packet_ack); + + // Fix the packet. + current_packet_dispositions_.pop_back(); + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_completions()); + EXPECT_TRUE(valid_packet_ack); + + // Tamper with the packet by changing the id. + QueueEvent(WebInputEvent::GestureScrollBegin); + StartFlush(); + int64 packet_ack_id = -1; + std::swap(current_packet_id_, packet_ack_id); + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_completions()); + EXPECT_FALSE(valid_packet_ack); + + // Fix the packet. + std::swap(current_packet_id_, packet_ack_id); + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(2, num_flush_completions()); + EXPECT_TRUE(valid_packet_ack); +} + +TEST_F(InputQueueTest, InjectedEventsAckedBeforeDidFinishFlush) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchMove); + + event_to_inject_ = CreateIPCInputEvent(new InputMsg_Copy(routing_id_)); + Flush(INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS); + EXPECT_EQ(0, num_flush_completions()); + + // The injected event should now be in the event packet. + EXPECT_EQ(1u, current_packet_dispositions_.size()); + EXPECT_EQ(1u, acked_followup_dispositions_.size()); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED)); + EXPECT_EQ(1, num_flush_completions()); + + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchStart); + event_to_inject_ = CreateWebInputEvent(WebInputEvent::TouchMove); + Flush(INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS); + // |event_to_inject_| is now in the event packet. + EXPECT_EQ(1u, acked_followup_dispositions_.size()); + EXPECT_EQ(1u, current_packet_dispositions_.size()); + + event_to_inject_ = CreateWebInputEvent(WebInputEvent::TouchMove); + // the next |event_to_inject_| is now in the event packet. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS)); + + EXPECT_EQ(2u, acked_followup_dispositions_.size()); + EXPECT_EQ(1u, current_packet_dispositions_.size()); + EXPECT_EQ(1, num_flush_completions()); + + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED)); + EXPECT_EQ(2, num_flush_completions()); +} + +} // namespace +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router.h b/chromium/content/browser/renderer_host/input/input_router.h index 2d8a4c6dd5d..5e4533fc1dd 100644 --- a/chromium/content/browser/renderer_host/input/input_router.h +++ b/chromium/content/browser/renderer_host/input/input_router.h @@ -24,10 +24,14 @@ class InputRouter : public IPC::Listener { public: virtual ~InputRouter() {} + // Should be called only in response to |SetNeedsFlush| requests made via + // the |InputRouterClient|. + virtual void Flush() = 0; + // Send and take ownership of the the given InputMsg_*. This should be used // only for event types not associated with a WebInputEvent. Returns true on // success and false otherwise. - virtual bool SendInput(IPC::Message* message) = 0; + virtual bool SendInput(scoped_ptr<IPC::Message> message) = 0; // WebInputEvents virtual void SendMouseEvent( @@ -60,9 +64,6 @@ class InputRouter : public IPC::Listener { // |gesture_event| to the router. virtual bool ShouldForwardGestureEvent( const GestureEventWithLatencyInfo& gesture_event) const = 0; - - // Returns |true| if the router has any queued or in-flight gesture events. - virtual bool HasQueuedGestureEvents() const = 0; }; } // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router_client.h b/chromium/content/browser/renderer_host/input/input_router_client.h index 9c854224b52..6479332d444 100644 --- a/chromium/content/browser/renderer_host/input/input_router_client.h +++ b/chromium/content/browser/renderer_host/input/input_router_client.h @@ -61,16 +61,15 @@ class CONTENT_EXPORT InputRouterClient { virtual bool OnSendGestureEventImmediately( const GestureEventWithLatencyInfo& gesture_event) = 0; - // Called upon event ack receipt from the renderer. - virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, - InputEventAckState ack_result) = 0; - virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, - InputEventAckState ack_result) = 0; - virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, - InputEventAckState ack_result) = 0; - virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, - InputEventAckState ack_result) = 0; - virtual void OnUnexpectedEventAck(bool bad_message) = 0; + // Certain router implementations require periodic flushing of queued events. + // When this method is called, the client should ensure a timely call, either + // synchronous or asynchronous, of |Flush| on the InputRouter. + virtual void SetNeedsFlush() = 0; + + // Called when the router has finished flushing all events queued at the time + // of the call to Flush. The call will typically be asynchronous with + // respect to the call to |Flush| on the InputRouter. + virtual void DidFlush() = 0; }; } // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router_unittest.cc b/chromium/content/browser/renderer_host/input/input_router_unittest.cc new file mode 100644 index 00000000000..da1ef1efb70 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_router_unittest.cc @@ -0,0 +1,225 @@ +// Copyright 2013 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/input/input_router_unittest.h" + +#include "content/browser/renderer_host/input/input_router.h" +#include "content/common/input_messages.h" +#include "ui/events/keycodes/keyboard_codes.h" + +#if defined(OS_WIN) || defined(USE_AURA) +#include "content/browser/renderer_host/ui_events_helper.h" +#include "ui/events/event.h" +#endif + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +InputRouterTest::InputRouterTest() {} +InputRouterTest::~InputRouterTest() {} + +void InputRouterTest::SetUp() { + browser_context_.reset(new TestBrowserContext()); + process_.reset(new MockRenderProcessHost(browser_context_.get())); + client_.reset(new MockInputRouterClient()); + ack_handler_.reset(new MockInputAckHandler()); + input_router_ = CreateInputRouter(process_.get(), + client_.get(), + ack_handler_.get(), + MSG_ROUTING_NONE); + client_->set_input_router(input_router_.get()); + ack_handler_->set_input_router(input_router_.get()); +} + +void InputRouterTest::TearDown() { + // Process all pending tasks to avoid InputRouterTest::leaks. + base::MessageLoop::current()->RunUntilIdle(); + + input_router_.reset(); + client_.reset(); + process_.reset(); + browser_context_.reset(); +} + +void InputRouterTest::SimulateKeyboardEvent(WebInputEvent::Type type) { + NativeWebKeyboardEvent key_event; + key_event.type = type; + key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value. + input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo()); + client_->ExpectSendCalled(true); + EXPECT_EQ(type, client_->sent_key_event().type); + EXPECT_EQ(key_event.windowsKeyCode, + client_->sent_key_event().windowsKeyCode); +} + +void InputRouterTest::SimulateWheelEvent(float dX, + float dY, + int modifiers, + bool precise) { + WebMouseWheelEvent wheel_event; + wheel_event.type = WebInputEvent::MouseWheel; + wheel_event.deltaX = dX; + wheel_event.deltaY = dY; + wheel_event.modifiers = modifiers; + wheel_event.hasPreciseScrollingDeltas = precise; + input_router_->SendWheelEvent( + MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); + client_->ExpectSendCalled(true); + EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); + EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX); +} + +void InputRouterTest::SimulateMouseMove(int x, int y, int modifiers) { + WebMouseEvent mouse_event; + mouse_event.type = WebInputEvent::MouseMove; + mouse_event.x = mouse_event.windowX = x; + mouse_event.y = mouse_event.windowY = y; + mouse_event.modifiers = modifiers; + input_router_->SendMouseEvent( + MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo())); + client_->ExpectSendCalled(true); + EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type); + EXPECT_EQ(x, client_->sent_mouse_event().event.x); +} + +void InputRouterTest::SimulateWheelEventWithPhase( + WebMouseWheelEvent::Phase phase) { + WebMouseWheelEvent wheel_event; + wheel_event.type = WebInputEvent::MouseWheel; + wheel_event.phase = phase; + input_router_->SendWheelEvent( + MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); + client_->ExpectSendCalled(true); + EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); + EXPECT_EQ(phase, client_->sent_wheel_event().event.phase); +} + +// Inject provided synthetic WebGestureEvent instance. +void InputRouterTest::SimulateGestureEventCore(WebInputEvent::Type type, + WebGestureEvent::SourceDevice sourceDevice, + WebGestureEvent* gesture_event) { + gesture_event->type = type; + gesture_event->sourceDevice = sourceDevice; + GestureEventWithLatencyInfo gesture_with_latency( + *gesture_event, ui::LatencyInfo()); + input_router_->SendGestureEvent(gesture_with_latency); + client_->ExpectSendCalled(true); + EXPECT_EQ(type, client_->sent_gesture_event().event.type); + EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice); +} + +// Inject simple synthetic WebGestureEvent instances. +void InputRouterTest::SimulateGestureEvent(WebInputEvent::Type type, + WebGestureEvent::SourceDevice sourceDevice) { + WebGestureEvent gesture_event; + SimulateGestureEventCore(type, sourceDevice, &gesture_event); +} + +void InputRouterTest::SimulateGestureScrollUpdateEvent(float dX, + float dY, + int modifiers) { + WebGestureEvent gesture_event; + gesture_event.data.scrollUpdate.deltaX = dX; + gesture_event.data.scrollUpdate.deltaY = dY; + gesture_event.modifiers = modifiers; + SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate, + WebGestureEvent::Touchscreen, &gesture_event); +} + +void InputRouterTest::SimulateGesturePinchUpdateEvent(float scale, + float anchorX, + float anchorY, + int modifiers) { + WebGestureEvent gesture_event; + gesture_event.data.pinchUpdate.scale = scale; + gesture_event.x = anchorX; + gesture_event.y = anchorY; + gesture_event.modifiers = modifiers; + SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate, + WebGestureEvent::Touchscreen, &gesture_event); +} + +// Inject synthetic GestureFlingStart events. +void InputRouterTest::SimulateGestureFlingStartEvent( + float velocityX, + float velocityY, + WebGestureEvent::SourceDevice sourceDevice) { + WebGestureEvent gesture_event; + gesture_event.data.flingStart.velocityX = velocityX; + gesture_event.data.flingStart.velocityY = velocityY; + SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice, + &gesture_event); +} + +void InputRouterTest::SimulateTouchEvent( + int x, + int y) { + PressTouchPoint(x, y); + SendTouchEvent(); +} + +// Set the timestamp for the touch-event. +void InputRouterTest::SetTouchTimestamp(base::TimeDelta timestamp) { + touch_event_.timeStampSeconds = timestamp.InSecondsF(); +} + +// Sends a touch event (irrespective of whether the page has a touch-event +// handler or not). +void InputRouterTest::SendTouchEvent() { + input_router_->SendTouchEvent( + TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo())); + + // Mark all the points as stationary. And remove the points that have been + // released. + int point = 0; + for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) { + if (touch_event_.touches[i].state == WebTouchPoint::StateReleased) + continue; + + touch_event_.touches[point] = touch_event_.touches[i]; + touch_event_.touches[point].state = + WebTouchPoint::StateStationary; + ++point; + } + touch_event_.touchesLength = point; + touch_event_.type = WebInputEvent::Undefined; +} + +int InputRouterTest::PressTouchPoint(int x, int y) { + if (touch_event_.touchesLength == touch_event_.touchesLengthCap) + return -1; + WebTouchPoint& point = + touch_event_.touches[touch_event_.touchesLength]; + point.id = touch_event_.touchesLength; + point.position.x = point.screenPosition.x = x; + point.position.y = point.screenPosition.y = y; + point.state = WebTouchPoint::StatePressed; + point.radiusX = point.radiusY = 1.f; + ++touch_event_.touchesLength; + touch_event_.type = WebInputEvent::TouchStart; + return point.id; +} + +void InputRouterTest::MoveTouchPoint(int index, int x, int y) { + CHECK(index >= 0 && index < touch_event_.touchesLengthCap); + WebTouchPoint& point = touch_event_.touches[index]; + point.position.x = point.screenPosition.x = x; + point.position.y = point.screenPosition.y = y; + touch_event_.touches[index].state = WebTouchPoint::StateMoved; + touch_event_.type = WebInputEvent::TouchMove; +} + +void InputRouterTest::ReleaseTouchPoint(int index) { + CHECK(index >= 0 && index < touch_event_.touchesLengthCap); + touch_event_.touches[index].state = WebTouchPoint::StateReleased; + touch_event_.type = WebInputEvent::TouchEnd; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router_unittest.h b/chromium/content/browser/renderer_host/input/input_router_unittest.h new file mode 100644 index 00000000000..20a68af3fb1 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_router_unittest.h @@ -0,0 +1,83 @@ +// Copyright 2013 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_INPUT_INPUT_ROUTER_UNITTEST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_UNITTEST_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/input_router_client.h" +#include "content/browser/renderer_host/input/mock_input_ack_handler.h" +#include "content/browser/renderer_host/input/mock_input_router_client.h" +#include "content/public/test/mock_render_process_host.h" +#include "content/public/test/test_browser_context.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +namespace content { + +class InputRouter; +class MockInputRouterClient; + +class InputRouterTest : public testing::Test { + public: + InputRouterTest(); + virtual ~InputRouterTest(); + + protected: + // Called on SetUp. + virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* handler, + int routing_id) = 0; + + // testing::Test + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + void SendInputEventACK(WebKit::WebInputEvent::Type type, + InputEventAckState ack_result); + void SimulateKeyboardEvent(WebKit::WebInputEvent::Type type); + void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise); + void SimulateMouseMove(int x, int y, int modifiers); + void SimulateWheelEventWithPhase(WebKit::WebMouseWheelEvent::Phase phase); + void SimulateGestureEventCore(WebKit::WebInputEvent::Type type, + WebKit::WebGestureEvent::SourceDevice sourceDevice, + WebKit::WebGestureEvent* gesture_event); + void SimulateGestureEvent(WebKit::WebInputEvent::Type type, + WebKit::WebGestureEvent::SourceDevice sourceDevice); + void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers); + void SimulateGesturePinchUpdateEvent(float scale, + float anchorX, + float anchorY, + int modifiers); + void SimulateGestureFlingStartEvent( + float velocityX, + float velocityY, + WebKit::WebGestureEvent::SourceDevice sourceDevice); + void SimulateTouchEvent(int x, int y); + void SetTouchTimestamp(base::TimeDelta timestamp); + + // Sends a touch event (irrespective of whether the page has a touch-event + // handler or not). + void SendTouchEvent(); + + int PressTouchPoint(int x, int y); + void MoveTouchPoint(int index, int x, int y); + void ReleaseTouchPoint(int index); + + scoped_ptr<MockRenderProcessHost> process_; + scoped_ptr<MockInputRouterClient> client_; + scoped_ptr<MockInputAckHandler> ack_handler_; + scoped_ptr<InputRouter> input_router_; + + private: + base::MessageLoopForUI message_loop_; + WebKit::WebTouchEvent touch_event_; + + scoped_ptr<TestBrowserContext> browser_context_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_UNITTEST_H_ diff --git a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc new file mode 100644 index 00000000000..fc97ab3450b --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc @@ -0,0 +1,77 @@ +// Copyright 2013 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/input/mock_input_ack_handler.h" + +#include "content/browser/renderer_host/input/input_router.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +MockInputAckHandler::MockInputAckHandler() + : input_router_(NULL), + ack_count_(0), + unexpected_event_ack_called_(false), + ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} + +MockInputAckHandler::~MockInputAckHandler() {} + +void MockInputAckHandler::OnKeyboardEventAck( + const NativeWebKeyboardEvent& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_key_event_ = event; + RecordAckCalled(ack_result); +} + +void MockInputAckHandler::OnWheelEventAck( + const WebMouseWheelEvent& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_wheel_event_ = event; + RecordAckCalled(ack_result); +} + +void MockInputAckHandler::OnTouchEventAck( + const TouchEventWithLatencyInfo& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_touch_event_ = event; + RecordAckCalled(ack_result); + if (touch_followup_event_) + input_router_->SendGestureEvent(*touch_followup_event_); +} + +void MockInputAckHandler::OnGestureEventAck( + const WebGestureEvent& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_gesture_event_ = event; + RecordAckCalled(ack_result); +} + +void MockInputAckHandler::OnUnexpectedEventAck(UnexpectedEventAckType type) { + VLOG(1) << __FUNCTION__ << " called!"; + unexpected_event_ack_called_ = true; +} + +void MockInputAckHandler::ExpectAckCalled(int times) { + EXPECT_EQ(times, ack_count_); + ack_count_ = 0; +} + +void MockInputAckHandler::RecordAckCalled(InputEventAckState ack_result) { + ++ack_count_; + ack_state_ = ack_result; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h new file mode 100644 index 00000000000..604b26b94cb --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h @@ -0,0 +1,77 @@ +// Copyright 2013 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_INPUT_MOCK_INPUT_ACK_HANDLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" + +namespace content { + +class InputRouter; + +class MockInputAckHandler : public InputAckHandler { + public: + MockInputAckHandler(); + virtual ~MockInputAckHandler(); + + // InputAckHandler + virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE; + + void ExpectAckCalled(int times); + + void set_input_router(InputRouter* input_router) { + input_router_ = input_router; + } + + void set_followup_touch_event(scoped_ptr<GestureEventWithLatencyInfo> event) { + touch_followup_event_ = event.Pass(); + } + + bool unexpected_event_ack_called() const { + return unexpected_event_ack_called_; + } + InputEventAckState ack_state() const { return ack_state_; } + + const NativeWebKeyboardEvent& acked_keyboard_event() const { + return acked_key_event_; + } + const WebKit::WebMouseWheelEvent& acked_wheel_event() const { + return acked_wheel_event_; + } + const TouchEventWithLatencyInfo& acked_touch_event() const { + return acked_touch_event_; + } + const WebKit::WebGestureEvent& acked_gesture_event() const { + return acked_gesture_event_; + } + + private: + void RecordAckCalled(InputEventAckState ack_result); + + InputRouter* input_router_; + + int ack_count_; + bool unexpected_event_ack_called_; + InputEventAckState ack_state_; + NativeWebKeyboardEvent acked_key_event_; + WebKit::WebMouseWheelEvent acked_wheel_event_; + TouchEventWithLatencyInfo acked_touch_event_; + WebKit::WebGestureEvent acked_gesture_event_; + + scoped_ptr<GestureEventWithLatencyInfo> touch_followup_event_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_ diff --git a/chromium/content/browser/renderer_host/input/mock_input_router_client.cc b/chromium/content/browser/renderer_host/input/mock_input_router_client.cc new file mode 100644 index 00000000000..86fb53b6b58 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_router_client.cc @@ -0,0 +1,148 @@ +// Copyright 2013 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/input/mock_input_router_client.h" + +#include "content/browser/renderer_host/input/input_router.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +MockInputRouterClient::MockInputRouterClient() + : input_router_(NULL), + in_flight_event_count_(0), + has_touch_handler_(false), + filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED), + is_shortcut_(false), + allow_send_event_(true), + send_called_(false), + send_immediately_called_(false), + did_flush_called_(false), + set_needs_flush_called_(false) {} + +MockInputRouterClient::~MockInputRouterClient() {} + +InputEventAckState MockInputRouterClient::FilterInputEvent( + const WebInputEvent& input_event, + const ui::LatencyInfo& latency_info) { + return filter_state_; +} + +void MockInputRouterClient::IncrementInFlightEventCount() { + ++in_flight_event_count_; +} + +void MockInputRouterClient::DecrementInFlightEventCount() { + --in_flight_event_count_; +} + +void MockInputRouterClient::OnHasTouchEventHandlers( + bool has_handlers) { + has_touch_handler_ = has_handlers; +} + +bool MockInputRouterClient::OnSendKeyboardEvent( + const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info, + bool* is_shortcut) { + send_called_ = true; + sent_key_event_ = key_event; + *is_shortcut = is_shortcut_; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) { + send_called_ = true; + sent_wheel_event_ = wheel_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) { + send_called_ = true; + sent_mouse_event_ = mouse_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) { + send_called_ = true; + sent_touch_event_ = touch_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) { + send_called_ = true; + sent_gesture_event_ = gesture_event; + + return allow_send_event_ && + input_router_->ShouldForwardGestureEvent(gesture_event); +} + +bool MockInputRouterClient::OnSendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) { + send_immediately_called_ = true; + immediately_sent_mouse_event_ = mouse_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) { + send_immediately_called_ = true; + immediately_sent_touch_event_ = touch_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) { + send_immediately_called_ = true; + immediately_sent_gesture_event_ = gesture_event; + return allow_send_event_; +} + +void MockInputRouterClient::ExpectSendCalled(bool called) { + EXPECT_EQ(called, send_called_); + send_called_ = false; +} + +void MockInputRouterClient::ExpectSendImmediatelyCalled(bool called) { + EXPECT_EQ(called, send_immediately_called_); + send_immediately_called_ = false; +} + +void MockInputRouterClient::ExpectNeedsFlushCalled(bool called) { + EXPECT_EQ(called, set_needs_flush_called_); + set_needs_flush_called_ = false; +} + +void MockInputRouterClient::ExpectDidFlushCalled(bool called) { + EXPECT_EQ(called, did_flush_called_); + did_flush_called_ = false; +} + +void MockInputRouterClient::DidFlush() { + did_flush_called_ = true; +} + +void MockInputRouterClient::SetNeedsFlush() { + set_needs_flush_called_ = true; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/mock_input_router_client.h b/chromium/content/browser/renderer_host/input/mock_input_router_client.h new file mode 100644 index 00000000000..a02f73891f0 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_router_client.h @@ -0,0 +1,126 @@ +// Copyright 2013 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_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/input_router_client.h" + +namespace content { + +class InputRouter; + +class MockInputRouterClient : public InputRouterClient { + public: + MockInputRouterClient(); + virtual ~MockInputRouterClient(); + + // InputRouterClient + virtual InputEventAckState FilterInputEvent( + const WebKit::WebInputEvent& input_event, + const ui::LatencyInfo& latency_info) OVERRIDE; + virtual void IncrementInFlightEventCount() OVERRIDE; + virtual void DecrementInFlightEventCount() OVERRIDE; + virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE; + virtual bool OnSendKeyboardEvent( + const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info, + bool* is_shortcut) OVERRIDE; + virtual bool OnSendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE; + virtual bool OnSendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual bool OnSendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual bool OnSendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual bool OnSendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual bool OnSendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual bool OnSendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual void DidFlush() OVERRIDE; + virtual void SetNeedsFlush() OVERRIDE; + + void ExpectSendCalled(bool called); + void ExpectSendImmediatelyCalled(bool called); + void ExpectNeedsFlushCalled(bool called); + void ExpectDidFlushCalled(bool called); + + void set_input_router(InputRouter* input_router) { + input_router_ = input_router; + } + + bool has_touch_handler() const { return has_touch_handler_; } + void set_filter_state(InputEventAckState filter_state) { + filter_state_ = filter_state; + } + int in_flight_event_count() const { + return in_flight_event_count_; + } + void set_is_shortcut(bool is_shortcut) { + is_shortcut_ = is_shortcut; + } + void set_allow_send_event(bool allow) { + allow_send_event_ = allow; + } + const NativeWebKeyboardEvent& sent_key_event() { + return sent_key_event_; + } + const MouseWheelEventWithLatencyInfo& sent_wheel_event() { + return sent_wheel_event_; + } + const MouseEventWithLatencyInfo& sent_mouse_event() { + return sent_mouse_event_; + } + const GestureEventWithLatencyInfo& sent_gesture_event() { + return sent_gesture_event_; + } + const MouseEventWithLatencyInfo& immediately_sent_mouse_event() { + return immediately_sent_mouse_event_; + } + const TouchEventWithLatencyInfo& immediately_sent_touch_event() { + return immediately_sent_touch_event_; + } + const GestureEventWithLatencyInfo& immediately_sent_gesture_event() { + return immediately_sent_gesture_event_; + } + + bool did_flush_called() const { return did_flush_called_; } + bool needs_flush_called() const { return set_needs_flush_called_; } + void set_followup_touch_event(scoped_ptr<GestureEventWithLatencyInfo> event) { + touch_followup_event_ = event.Pass(); + } + + private: + InputRouter* input_router_; + int in_flight_event_count_; + bool has_touch_handler_; + + InputEventAckState filter_state_; + + bool is_shortcut_; + bool allow_send_event_; + bool send_called_; + NativeWebKeyboardEvent sent_key_event_; + MouseWheelEventWithLatencyInfo sent_wheel_event_; + MouseEventWithLatencyInfo sent_mouse_event_; + TouchEventWithLatencyInfo sent_touch_event_; + GestureEventWithLatencyInfo sent_gesture_event_; + + bool send_immediately_called_; + MouseEventWithLatencyInfo immediately_sent_mouse_event_; + TouchEventWithLatencyInfo immediately_sent_touch_event_; + GestureEventWithLatencyInfo immediately_sent_gesture_event_; + + bool did_flush_called_; + bool set_needs_flush_called_; + scoped_ptr<GestureEventWithLatencyInfo> touch_followup_event_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_ diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue.cc b/chromium/content/browser/renderer_host/input/touch_event_queue.cc index c1675b9952d..e22c05d0f80 100644 --- a/chromium/content/browser/renderer_host/input/touch_event_queue.cc +++ b/chromium/content/browser/renderer_host/input/touch_event_queue.cc @@ -91,7 +91,8 @@ class CoalescedWebTouchEvent { TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client) : client_(client), - dispatching_touch_ack_(false) { + dispatching_touch_ack_(false), + no_touch_move_to_renderer_(false) { DCHECK(client); } @@ -212,6 +213,10 @@ bool TouchEventQueue::ShouldForwardToRenderer( if (event.type == WebKit::WebInputEvent::TouchStart) return true; + if (event.type == WebKit::WebInputEvent::TouchMove && + no_touch_move_to_renderer_) + return false; + for (unsigned int i = 0; i < event.touchesLength; ++i) { const WebKit::WebTouchPoint& point = event.touches[i]; // If a point has been stationary, then don't take it into account. diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue.h b/chromium/content/browser/renderer_host/input/touch_event_queue.h index 23dda66c38b..358fae9c50d 100644 --- a/chromium/content/browser/renderer_host/input/touch_event_queue.h +++ b/chromium/content/browser/renderer_host/input/touch_event_queue.h @@ -61,6 +61,10 @@ class TouchEventQueue { return touch_queue_.empty(); } + void set_no_touch_move_to_renderer(bool value) { + no_touch_move_to_renderer_ = value; + } + private: friend class MockRenderWidgetHost; friend class ImmediateInputRouterTest; @@ -88,6 +92,11 @@ class TouchEventQueue { // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|. bool dispatching_touch_ack_; + // Don't send touch move events to renderer. This is enabled when the page + // is scrolling. This behaviour is currently enabled only on aura behind a + // flag. + bool no_touch_move_to_renderer_; + DISALLOW_COPY_AND_ASSIGN(TouchEventQueue); }; diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc index eca820bb28b..b5c96c657eb 100644 --- a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc @@ -7,8 +7,8 @@ #include "base/logging.h" #include "content/browser/renderer_host/input/web_input_event_util.h" #include "content/browser/renderer_host/input/web_input_event_util_posix.h" -#include "ui/base/keycodes/keyboard_code_conversion_android.h" -#include "ui/base/keycodes/keyboard_codes_posix.h" +#include "ui/events/keycodes/keyboard_code_conversion_android.h" +#include "ui/events/keycodes/keyboard_codes_posix.h" namespace content { diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc new file mode 100644 index 00000000000..c101595c79f --- /dev/null +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc @@ -0,0 +1,603 @@ +// Copyright 2013 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/input/web_input_event_builders_gtk.h" + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "base/logging.h" +#include "content/browser/renderer_host/input/web_input_event_util_posix.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/keycodes/keyboard_code_conversion_gtk.h" + +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebKeyboardEvent; + +namespace { + +// For click count tracking. +static int num_clicks = 0; +static GdkWindow* last_click_event_window = 0; +static gint last_click_time = 0; +static gint last_click_x = 0; +static gint last_click_y = 0; +static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonNone; + +bool ShouldForgetPreviousClick(GdkWindow* window, gint time, gint x, gint y) { + static GtkSettings* settings = gtk_settings_get_default(); + + if (window != last_click_event_window) + return true; + + gint double_click_time = 250; + gint double_click_distance = 5; + g_object_get(G_OBJECT(settings), + "gtk-double-click-time", + &double_click_time, + "gtk-double-click-distance", + &double_click_distance, + NULL); + return (time - last_click_time) > double_click_time || + std::abs(x - last_click_x) > double_click_distance || + std::abs(y - last_click_y) > double_click_distance; +} + +void ResetClickCountState() { + num_clicks = 0; + last_click_event_window = 0; + last_click_time = 0; + last_click_x = 0; + last_click_y = 0; + last_click_button = WebKit::WebMouseEvent::ButtonNone; +} + +bool IsKeyPadKeyval(guint keyval) { + // Keypad keyvals all fall into one range. + return keyval >= GDK_KP_Space && keyval <= GDK_KP_9; +} + +double GdkEventTimeToWebEventTime(guint32 time) { + // Convert from time in ms to time in sec. + return time / 1000.0; +} + +int GdkStateToWebEventModifiers(guint state) { + int modifiers = 0; + if (state & GDK_SHIFT_MASK) + modifiers |= WebInputEvent::ShiftKey; + if (state & GDK_CONTROL_MASK) + modifiers |= WebInputEvent::ControlKey; + if (state & GDK_MOD1_MASK) + modifiers |= WebInputEvent::AltKey; + if (state & GDK_META_MASK) + modifiers |= WebInputEvent::MetaKey; + if (state & GDK_BUTTON1_MASK) + modifiers |= WebInputEvent::LeftButtonDown; + if (state & GDK_BUTTON2_MASK) + modifiers |= WebInputEvent::MiddleButtonDown; + if (state & GDK_BUTTON3_MASK) + modifiers |= WebInputEvent::RightButtonDown; + if (state & GDK_LOCK_MASK) + modifiers |= WebInputEvent::CapsLockOn; + if (state & GDK_MOD2_MASK) + modifiers |= WebInputEvent::NumLockOn; + return modifiers; +} + +ui::KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) { + static const unsigned int kHardwareCodeToGDKKeyval[] = { + 0, // 0x00: + 0, // 0x01: + 0, // 0x02: + 0, // 0x03: + 0, // 0x04: + 0, // 0x05: + 0, // 0x06: + 0, // 0x07: + 0, // 0x08: + 0, // 0x09: GDK_Escape + GDK_1, // 0x0A: GDK_1 + GDK_2, // 0x0B: GDK_2 + GDK_3, // 0x0C: GDK_3 + GDK_4, // 0x0D: GDK_4 + GDK_5, // 0x0E: GDK_5 + GDK_6, // 0x0F: GDK_6 + GDK_7, // 0x10: GDK_7 + GDK_8, // 0x11: GDK_8 + GDK_9, // 0x12: GDK_9 + GDK_0, // 0x13: GDK_0 + GDK_minus, // 0x14: GDK_minus + GDK_equal, // 0x15: GDK_equal + 0, // 0x16: GDK_BackSpace + 0, // 0x17: GDK_Tab + GDK_q, // 0x18: GDK_q + GDK_w, // 0x19: GDK_w + GDK_e, // 0x1A: GDK_e + GDK_r, // 0x1B: GDK_r + GDK_t, // 0x1C: GDK_t + GDK_y, // 0x1D: GDK_y + GDK_u, // 0x1E: GDK_u + GDK_i, // 0x1F: GDK_i + GDK_o, // 0x20: GDK_o + GDK_p, // 0x21: GDK_p + GDK_bracketleft, // 0x22: GDK_bracketleft + GDK_bracketright, // 0x23: GDK_bracketright + 0, // 0x24: GDK_Return + 0, // 0x25: GDK_Control_L + GDK_a, // 0x26: GDK_a + GDK_s, // 0x27: GDK_s + GDK_d, // 0x28: GDK_d + GDK_f, // 0x29: GDK_f + GDK_g, // 0x2A: GDK_g + GDK_h, // 0x2B: GDK_h + GDK_j, // 0x2C: GDK_j + GDK_k, // 0x2D: GDK_k + GDK_l, // 0x2E: GDK_l + GDK_semicolon, // 0x2F: GDK_semicolon + GDK_apostrophe, // 0x30: GDK_apostrophe + GDK_grave, // 0x31: GDK_grave + 0, // 0x32: GDK_Shift_L + GDK_backslash, // 0x33: GDK_backslash + GDK_z, // 0x34: GDK_z + GDK_x, // 0x35: GDK_x + GDK_c, // 0x36: GDK_c + GDK_v, // 0x37: GDK_v + GDK_b, // 0x38: GDK_b + GDK_n, // 0x39: GDK_n + GDK_m, // 0x3A: GDK_m + GDK_comma, // 0x3B: GDK_comma + GDK_period, // 0x3C: GDK_period + GDK_slash, // 0x3D: GDK_slash + 0, // 0x3E: GDK_Shift_R + 0, // 0x3F: + 0, // 0x40: + 0, // 0x41: + 0, // 0x42: + 0, // 0x43: + 0, // 0x44: + 0, // 0x45: + 0, // 0x46: + 0, // 0x47: + 0, // 0x48: + 0, // 0x49: + 0, // 0x4A: + 0, // 0x4B: + 0, // 0x4C: + 0, // 0x4D: + 0, // 0x4E: + 0, // 0x4F: + 0, // 0x50: + 0, // 0x51: + 0, // 0x52: + 0, // 0x53: + 0, // 0x54: + 0, // 0x55: + 0, // 0x56: + 0, // 0x57: + 0, // 0x58: + 0, // 0x59: + 0, // 0x5A: + 0, // 0x5B: + 0, // 0x5C: + 0, // 0x5D: + 0, // 0x5E: + 0, // 0x5F: + 0, // 0x60: + 0, // 0x61: + 0, // 0x62: + 0, // 0x63: + 0, // 0x64: + 0, // 0x65: + 0, // 0x66: + 0, // 0x67: + 0, // 0x68: + 0, // 0x69: + 0, // 0x6A: + 0, // 0x6B: + 0, // 0x6C: + 0, // 0x6D: + 0, // 0x6E: + 0, // 0x6F: + 0, // 0x70: + 0, // 0x71: + 0, // 0x72: + GDK_Super_L, // 0x73: GDK_Super_L + GDK_Super_R, // 0x74: GDK_Super_R + }; + + // |windows_key_code| has to include a valid virtual-key code even when we + // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard + // on the Hebrew layout, |windows_key_code| should be VK_A. + // On the other hand, |event->keyval| value depends on the current + // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on + // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this + // ui::WindowsKeyCodeForGdkKeyCode() call returns 0. + // To improve compatibilty with Windows, we use |event->hardware_keycode| + // for retrieving its Windows key-code for the keys when the + // WebCore::windows_key_codeForEvent() call returns 0. + // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap + // objects cannot change because |event->hardware_keycode| doesn't change + // even when we change the layout options, e.g. when we swap a control + // key and a caps-lock key, GTK doesn't swap their + // |event->hardware_keycode| values but swap their |event->keyval| values. + ui::KeyboardCode windows_key_code = + ui::WindowsKeyCodeForGdkKeyCode(event->keyval); + if (windows_key_code) + return windows_key_code; + + if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) { + int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode]; + if (keyval) + return ui::WindowsKeyCodeForGdkKeyCode(keyval); + } + + // This key is one that keyboard-layout drivers cannot change. + // Use |event->keyval| to retrieve its |windows_key_code| value. + return ui::WindowsKeyCodeForGdkKeyCode(event->keyval); +} + +// Normalizes event->state to make it Windows/Mac compatible. Since the way +// of setting modifier mask on X is very different than Windows/Mac as shown +// in http://crbug.com/127142#c8, the normalization is necessary. +guint NormalizeEventState(const GdkEventKey* event) { + guint mask = 0; + switch (GdkEventToWindowsKeyCode(event)) { + case ui::VKEY_CONTROL: + case ui::VKEY_LCONTROL: + case ui::VKEY_RCONTROL: + mask = GDK_CONTROL_MASK; + break; + case ui::VKEY_SHIFT: + case ui::VKEY_LSHIFT: + case ui::VKEY_RSHIFT: + mask = GDK_SHIFT_MASK; + break; + case ui::VKEY_MENU: + case ui::VKEY_LMENU: + case ui::VKEY_RMENU: + mask = GDK_MOD1_MASK; + break; + case ui::VKEY_CAPITAL: + mask = GDK_LOCK_MASK; + break; + default: + return event->state; + } + if (event->type == GDK_KEY_PRESS) + return event->state | mask; + return event->state & ~mask; +} + +// Gets the corresponding control character of a specified key code. See: +// http://en.wikipedia.org/wiki/Control_characters +// We emulate Windows behavior here. +int GetControlCharacter(ui::KeyboardCode windows_key_code, bool shift) { + if (windows_key_code >= ui::VKEY_A && windows_key_code <= ui::VKEY_Z) { + // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A + return windows_key_code - ui::VKEY_A + 1; + } + if (shift) { + // following graphics chars require shift key to input. + switch (windows_key_code) { + // ctrl-@ maps to \x00 (Null byte) + case ui::VKEY_2: + return 0; + // ctrl-^ maps to \x1E (Record separator, Information separator two) + case ui::VKEY_6: + return 0x1E; + // ctrl-_ maps to \x1F (Unit separator, Information separator one) + case ui::VKEY_OEM_MINUS: + return 0x1F; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + return 0; + } + } else { + switch (windows_key_code) { + // ctrl-[ maps to \x1B (Escape) + case ui::VKEY_OEM_4: + return 0x1B; + // ctrl-\ maps to \x1C (File separator, Information separator four) + case ui::VKEY_OEM_5: + return 0x1C; + // ctrl-] maps to \x1D (Group separator, Information separator three) + case ui::VKEY_OEM_6: + return 0x1D; + // ctrl-Enter maps to \x0A (Line feed) + case ui::VKEY_RETURN: + return 0x0A; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + return 0; + } + } +} + +} // namespace + +namespace content { + +// WebKeyboardEvent ----------------------------------------------------------- + +WebKeyboardEvent WebKeyboardEventBuilder::Build(const GdkEventKey* event) { + WebKeyboardEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(NormalizeEventState(event)); + + switch (event->type) { + case GDK_KEY_RELEASE: + result.type = WebInputEvent::KeyUp; + break; + case GDK_KEY_PRESS: + result.type = WebInputEvent::RawKeyDown; + break; + default: + NOTREACHED(); + } + + // According to MSDN: + // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx + // Key events with Alt modifier and F10 are system key events. + // We just emulate this behavior. It's necessary to prevent webkit from + // processing keypress event generated by alt-d, etc. + // F10 is not special on Linux, so don't treat it as system key. + if (result.modifiers & WebInputEvent::AltKey) + result.isSystemKey = true; + + // The key code tells us which physical key was pressed (for example, the + // A key went down or up). It does not determine whether A should be lower + // or upper case. This is what text does, which should be the keyval. + ui::KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event); + result.windowsKeyCode = GetWindowsKeyCodeWithoutLocation(windows_key_code); + result.modifiers |= GetLocationModifiersFromWindowsKeyCode(windows_key_code); + result.nativeKeyCode = event->hardware_keycode; + + if (result.windowsKeyCode == ui::VKEY_RETURN) { + // We need to treat the enter key as a key press of character \r. This + // is apparently just how webkit handles it and what it expects. + result.unmodifiedText[0] = '\r'; + } else { + // FIXME: fix for non BMP chars + result.unmodifiedText[0] = + static_cast<int>(gdk_keyval_to_unicode(event->keyval)); + } + + // If ctrl key is pressed down, then control character shall be input. + if (result.modifiers & WebInputEvent::ControlKey) { + result.text[0] = + GetControlCharacter(ui::KeyboardCode(result.windowsKeyCode), + result.modifiers & WebInputEvent::ShiftKey); + } else { + result.text[0] = result.unmodifiedText[0]; + } + + result.setKeyIdentifierFromWindowsKeyCode(); + + // FIXME: Do we need to set IsAutoRepeat? + if (IsKeyPadKeyval(event->keyval)) + result.modifiers |= WebInputEvent::IsKeyPad; + + return result; +} + +WebKeyboardEvent WebKeyboardEventBuilder::Build(wchar_t character, + int state, + double timeStampSeconds) { + // keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and + // it is hard to use/ it from signal handlers which don't use GdkEventKey + // objects (e.g. GtkIMContext signal handlers.) For such handlers, this + // function creates a WebInputEvent::Char event without using a + // GdkEventKey object. + WebKeyboardEvent result; + result.type = WebKit::WebInputEvent::Char; + result.timeStampSeconds = timeStampSeconds; + result.modifiers = GdkStateToWebEventModifiers(state); + result.windowsKeyCode = character; + result.nativeKeyCode = character; + result.text[0] = character; + result.unmodifiedText[0] = character; + + // According to MSDN: + // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx + // Key events with Alt modifier and F10 are system key events. + // We just emulate this behavior. It's necessary to prevent webkit from + // processing keypress event generated by alt-d, etc. + // F10 is not special on Linux, so don't treat it as system key. + if (result.modifiers & WebInputEvent::AltKey) + result.isSystemKey = true; + + return result; +} + +// WebMouseEvent -------------------------------------------------------------- + +WebMouseEvent WebMouseEventBuilder::Build(const GdkEventButton* event) { + WebMouseEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + result.clickCount = 0; + + switch (event->type) { + case GDK_BUTTON_PRESS: + result.type = WebInputEvent::MouseDown; + break; + case GDK_BUTTON_RELEASE: + result.type = WebInputEvent::MouseUp; + break; + case GDK_3BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + default: + NOTREACHED(); + } + + result.button = WebMouseEvent::ButtonNone; + if (event->button == 1) + result.button = WebMouseEvent::ButtonLeft; + else if (event->button == 2) + result.button = WebMouseEvent::ButtonMiddle; + else if (event->button == 3) + result.button = WebMouseEvent::ButtonRight; + + if (result.type == WebInputEvent::MouseDown) { + bool forgetPreviousClick = ShouldForgetPreviousClick( + event->window, event->time, event->x, event->y); + + if (!forgetPreviousClick && result.button == last_click_button) { + ++num_clicks; + } else { + num_clicks = 1; + + last_click_event_window = event->window; + last_click_x = event->x; + last_click_y = event->y; + last_click_button = result.button; + } + last_click_time = event->time; + } + result.clickCount = num_clicks; + + return result; +} + +WebMouseEvent WebMouseEventBuilder::Build(const GdkEventMotion* event) { + WebMouseEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + + switch (event->type) { + case GDK_MOTION_NOTIFY: + result.type = WebInputEvent::MouseMove; + break; + default: + NOTREACHED(); + } + + result.button = WebMouseEvent::ButtonNone; + if (event->state & GDK_BUTTON1_MASK) + result.button = WebMouseEvent::ButtonLeft; + else if (event->state & GDK_BUTTON2_MASK) + result.button = WebMouseEvent::ButtonMiddle; + else if (event->state & GDK_BUTTON3_MASK) + result.button = WebMouseEvent::ButtonRight; + + if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y)) + ResetClickCountState(); + + return result; +} + +WebMouseEvent WebMouseEventBuilder::Build(const GdkEventCrossing* event) { + WebMouseEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + + switch (event->type) { + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + // Note that if we sent MouseEnter or MouseLeave to WebKit, it + // wouldn't work - they don't result in the proper JavaScript events. + // MouseMove does the right thing. + result.type = WebInputEvent::MouseMove; + break; + default: + NOTREACHED(); + } + + result.button = WebMouseEvent::ButtonNone; + if (event->state & GDK_BUTTON1_MASK) + result.button = WebMouseEvent::ButtonLeft; + else if (event->state & GDK_BUTTON2_MASK) + result.button = WebMouseEvent::ButtonMiddle; + else if (event->state & GDK_BUTTON3_MASK) + result.button = WebMouseEvent::ButtonRight; + + if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y)) + ResetClickCountState(); + + return result; +} + +// WebMouseWheelEvent --------------------------------------------------------- + +float WebMouseWheelEventBuilder::ScrollbarPixelsPerTick() { + // How much should we scroll per mouse wheel event? + // - Windows uses 3 lines by default and obeys a system setting. + // - Mozilla has a pref that lets you either use the "system" number of lines + // to scroll, or lets the user override it. + // For the "system" number of lines, it appears they've hardcoded 3. + // See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp + // and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp . + // - Gtk makes the scroll amount a function of the size of the scroll bar, + // which is not available to us here. + // Instead, we pick a number that empirically matches Firefox's behavior. + return 160.0f / 3.0f; +} + +WebMouseWheelEvent WebMouseWheelEventBuilder::Build( + const GdkEventScroll* event) { + WebMouseWheelEvent result; + + result.type = WebInputEvent::MouseWheel; + result.button = WebMouseEvent::ButtonNone; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + + static const float scrollbarPixelsPerTick = ScrollbarPixelsPerTick(); + switch (event->direction) { + case GDK_SCROLL_UP: + result.deltaY = scrollbarPixelsPerTick; + result.wheelTicksY = 1; + break; + case GDK_SCROLL_DOWN: + result.deltaY = -scrollbarPixelsPerTick; + result.wheelTicksY = -1; + break; + case GDK_SCROLL_LEFT: + result.deltaX = scrollbarPixelsPerTick; + result.wheelTicksX = 1; + break; + case GDK_SCROLL_RIGHT: + result.deltaX = -scrollbarPixelsPerTick; + result.wheelTicksX = -1; + break; + } + + return result; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h new file mode 100644 index 00000000000..6fe16429b35 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_ + +#include "content/common/content_export.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/keycodes/keyboard_codes.h" + +typedef struct _GdkEventButton GdkEventButton; +typedef struct _GdkEventMotion GdkEventMotion; +typedef struct _GdkEventCrossing GdkEventCrossing; +typedef struct _GdkEventScroll GdkEventScroll; +typedef struct _GdkEventKey GdkEventKey; + +namespace content { + +class CONTENT_EXPORT WebKeyboardEventBuilder { + public: + static WebKit::WebKeyboardEvent Build(const GdkEventKey* event); + static WebKit::WebKeyboardEvent Build(wchar_t character, + int state, + double time_secs); +}; + +class CONTENT_EXPORT WebMouseEventBuilder { + public: + static WebKit::WebMouseEvent Build(const GdkEventButton* event); + static WebKit::WebMouseEvent Build(const GdkEventMotion* event); + static WebKit::WebMouseEvent Build(const GdkEventCrossing* event); +}; + +class CONTENT_EXPORT WebMouseWheelEventBuilder { + public: + static float ScrollbarPixelsPerTick(); + static WebKit::WebMouseWheelEvent Build( + const GdkEventScroll* event); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc new file mode 100644 index 00000000000..9498c9eb1be --- /dev/null +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc @@ -0,0 +1,171 @@ +// Copyright 2013 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/input/web_input_event_builders_gtk.h" + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/keycodes/keyboard_code_conversion_gtk.h" + +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using content::WebMouseEventBuilder; +using content::WebKeyboardEventBuilder; + +namespace { + +TEST(WebMouseEventBuilderTest, DoubleClick) { + GdkEventButton first_click; + first_click.type = GDK_BUTTON_PRESS; + first_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1)); + first_click.x = first_click.y = first_click.x_root = first_click.y_root = 100; + first_click.state = 0; + first_click.time = 0; + first_click.button = 1; + + // Single click works. + WebMouseEvent first_click_events = + WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Make sure double click works. + GdkEventButton second_click = first_click; + second_click.time = first_click.time + 100; + WebMouseEvent second_click_events = + WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(2, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks with a long gap in between aren't counted as a double click. + second_click = first_click; + second_click.time = first_click.time + 1000; + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks far apart (horizontally) aren't counted as a double click. + second_click = first_click; + second_click.time = first_click.time + 1; + second_click.x = first_click.x + 100; + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks far apart (vertically) aren't counted as a double click. + second_click = first_click; + second_click.time = first_click.time + 1; + second_click.x = first_click.y + 100; + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks on different windows aren't a double click. + second_click = first_click; + second_click.time = first_click.time + 1; + second_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(2)); + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); +} + +TEST(WebMouseEventBuilderTest, MouseUpClickCount) { + GdkEventButton mouse_down; + memset(&mouse_down, 0, sizeof(mouse_down)); + mouse_down.type = GDK_BUTTON_PRESS; + mouse_down.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1)); + mouse_down.x = mouse_down.y = mouse_down.x_root = mouse_down.y_root = 100; + mouse_down.time = 0; + mouse_down.button = 1; + + // Properly set the last click time, so that the internal state won't be + // affected by previous tests. + WebMouseEventBuilder::Build(&mouse_down); + + mouse_down.time += 10000; + GdkEventButton mouse_up = mouse_down; + mouse_up.type = GDK_BUTTON_RELEASE; + WebMouseEvent mouse_down_event; + WebMouseEvent mouse_up_event; + + // Click for three times. + for (int i = 1; i < 4; ++i) { + mouse_down.time += 100; + mouse_down_event = WebMouseEventBuilder::Build(&mouse_down); + EXPECT_EQ(i, mouse_down_event.clickCount); + + mouse_up.time = mouse_down.time + 50; + mouse_up_event = WebMouseEventBuilder::Build(&mouse_up); + EXPECT_EQ(i, mouse_up_event.clickCount); + } + + // Reset the click count. + mouse_down.time += 10000; + mouse_down_event = WebMouseEventBuilder::Build(&mouse_down); + EXPECT_EQ(1, mouse_down_event.clickCount); + + // Moving the cursor for a significant distance will reset the click count to + // 0. + GdkEventMotion mouse_move; + memset(&mouse_move, 0, sizeof(mouse_move)); + mouse_move.type = GDK_MOTION_NOTIFY; + mouse_move.window = mouse_down.window; + mouse_move.time = mouse_down.time; + mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root = + mouse_down.x + 100; + WebMouseEventBuilder::Build(&mouse_move); + + mouse_up.time = mouse_down.time + 50; + mouse_up_event = WebMouseEventBuilder::Build(&mouse_up); + EXPECT_EQ(0, mouse_up_event.clickCount); + + // Reset the click count. + mouse_down.time += 10000; + mouse_down_event = WebMouseEventBuilder::Build(&mouse_down); + EXPECT_EQ(1, mouse_down_event.clickCount); + + // Moving the cursor with a significant delay will reset the click count to 0. + mouse_move.time = mouse_down.time + 1000; + mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root = + mouse_down.x; + WebMouseEventBuilder::Build(&mouse_move); + + mouse_up.time = mouse_move.time + 50; + mouse_up_event = WebMouseEventBuilder::Build(&mouse_up); + EXPECT_EQ(0, mouse_up_event.clickCount); +} + +TEST(WebKeyboardEventBuilderTest, NumPadConversion) { + // Construct a GDK input event for the numpad "5" key. + char five[] = "5"; + GdkEventKey gdk_event; + memset(&gdk_event, 0, sizeof(GdkEventKey)); + gdk_event.type = GDK_KEY_PRESS; + gdk_event.keyval = GDK_KP_5; + gdk_event.string = five; + + // Numpad flag should be set on the WebKeyboardEvent. + WebKeyboardEvent web_event = WebKeyboardEventBuilder::Build(&gdk_event); + EXPECT_TRUE(web_event.modifiers & WebInputEvent::IsKeyPad); +} + +} // anonymous namespace diff --git a/chromium/content/browser/renderer_host/input/web_input_event_util.h b/chromium/content/browser/renderer_host/input/web_input_event_util.h index 75870ebc7cc..1abaf8cf34e 100644 --- a/chromium/content/browser/renderer_host/input/web_input_event_util.h +++ b/chromium/content/browser/renderer_host/input/web_input_event_util.h @@ -6,7 +6,7 @@ #define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_UTIL_H_ #include "content/common/content_export.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace WebKit { class WebKeyboardEvent; diff --git a/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h b/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h index 34e4ee96165..e7dc1549cb9 100644 --- a/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h +++ b/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h @@ -6,7 +6,7 @@ #define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_UTIL_POSIX_H_ #include "third_party/WebKit/public/web/WebInputEvent.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace content { diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc b/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc index b4959567a31..50bb3ec8310 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc @@ -34,7 +34,8 @@ AudioInputDeviceManager::AudioInputDeviceManager( StreamDeviceInfo fake_device(MEDIA_DEVICE_AUDIO_CAPTURE, media::AudioManagerBase::kDefaultDeviceName, media::AudioManagerBase::kDefaultDeviceId, - 44100, media::CHANNEL_LAYOUT_STEREO, false); + 44100, media::CHANNEL_LAYOUT_STEREO, + 0, false); fake_device.session_id = kFakeOpenSessionId; devices_.push_back(fake_device); } @@ -169,19 +170,37 @@ void AudioInputDeviceManager::OpenOnDeviceThread( DCHECK(IsOnDeviceThread()); StreamDeviceInfo out(info.device.type, info.device.name, info.device.id, - 0, 0, false); + 0, 0, 0, false); out.session_id = session_id; + + MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input; + if (use_fake_device_) { // Don't need to query the hardware information if using fake device. - out.device.sample_rate = 44100; - out.device.channel_layout = media::CHANNEL_LAYOUT_STEREO; + input_params.sample_rate = 44100; + input_params.channel_layout = media::CHANNEL_LAYOUT_STEREO; } else { // Get the preferred sample rate and channel configuration for the // audio device. media::AudioParameters params = audio_manager_->GetInputStreamParameters(info.device.id); - out.device.sample_rate = params.sample_rate(); - out.device.channel_layout = params.channel_layout(); + input_params.sample_rate = params.sample_rate(); + input_params.channel_layout = params.channel_layout(); + input_params.frames_per_buffer = params.frames_per_buffer(); + + // Add preferred output device information if a matching output device + // exists. + out.device.matched_output_device_id = + audio_manager_->GetAssociatedOutputDeviceID(info.device.id); + if (!out.device.matched_output_device_id.empty()) { + params = audio_manager_->GetOutputStreamParameters( + out.device.matched_output_device_id); + MediaStreamDevice::AudioDeviceParameters& matched_output_params = + out.device.matched_output; + matched_output_params.sample_rate = params.sample_rate(); + matched_output_params.channel_layout = params.channel_layout(); + matched_output_params.frames_per_buffer = params.frames_per_buffer(); + } } // Return the |session_id| through the listener by posting a task on @@ -206,6 +225,7 @@ void AudioInputDeviceManager::OpenedOnIOThread(int session_id, DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_EQ(session_id, info.session_id); DCHECK(GetDevice(session_id) == devices_.end()); + devices_.push_back(info); if (listener_) diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc index 03b31d2845e..25d8a1722e9 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc @@ -177,7 +177,8 @@ TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) { int sample_rate(0); int channel_config(0); StreamDeviceInfo dummy_device( - stream_type, device_name, device_id, sample_rate, channel_config, false); + stream_type, device_name, device_id, sample_rate, channel_config, 2048, + false); int session_id = manager_->Open(dummy_device); EXPECT_CALL(*audio_input_listener_, diff --git a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc index 4a2f731a81c..1c021095676 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc @@ -51,11 +51,12 @@ AudioInputRendererHost::AudioEntry::~AudioEntry() {} AudioInputRendererHost::AudioInputRendererHost( media::AudioManager* audio_manager, MediaStreamManager* media_stream_manager, - AudioMirroringManager* audio_mirroring_manager) + AudioMirroringManager* audio_mirroring_manager, + media::UserInputMonitor* user_input_monitor) : audio_manager_(audio_manager), media_stream_manager_(media_stream_manager), - audio_mirroring_manager_(audio_mirroring_manager) { -} + audio_mirroring_manager_(audio_mirroring_manager), + user_input_monitor_(user_input_monitor) {} AudioInputRendererHost::~AudioInputRendererHost() { DCHECK(audio_entries_.empty()); @@ -272,20 +273,23 @@ void AudioInputRendererHost::OnCreateStream( entry->controller = media::AudioInputController::CreateForStream( audio_manager_->GetMessageLoop(), this, - WebContentsAudioInputStream::Create( - device_id, audio_params, audio_manager_->GetWorkerLoop(), - audio_mirroring_manager_), - entry->writer.get()); + WebContentsAudioInputStream::Create(device_id, + audio_params, + audio_manager_->GetWorkerLoop(), + audio_mirroring_manager_), + entry->writer.get(), + user_input_monitor_); } else { // TODO(henrika): replace CreateLowLatency() with Create() as soon // as satish has ensured that Speech Input also uses the default low- // latency path. See crbug.com/112472 for details. - entry->controller = media::AudioInputController::CreateLowLatency( - audio_manager_, - this, - audio_params, - device_id, - entry->writer.get()); + entry->controller = + media::AudioInputController::CreateLowLatency(audio_manager_, + this, + audio_params, + device_id, + entry->writer.get(), + user_input_monitor_); } if (!entry->controller.get()) { diff --git a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h index d16ebfad86a..5df1fc6f2f0 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h +++ b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h @@ -44,6 +44,7 @@ namespace media { class AudioManager; class AudioParameters; +class UserInputMonitor; } namespace content { @@ -55,10 +56,11 @@ class CONTENT_EXPORT AudioInputRendererHost public media::AudioInputController::EventHandler { public: // Called from UI thread from the owner of this object. - AudioInputRendererHost( - media::AudioManager* audio_manager, - MediaStreamManager* media_stream_manager, - AudioMirroringManager* audio_mirroring_manager); + // |user_input_monitor| is used for typing detection and can be NULL. + AudioInputRendererHost(media::AudioManager* audio_manager, + MediaStreamManager* media_stream_manager, + AudioMirroringManager* audio_mirroring_manager, + media::UserInputMonitor* user_input_monitor); // BrowserMessageFilter implementation. virtual void OnChannelClosing() OVERRIDE; @@ -154,6 +156,9 @@ class CONTENT_EXPORT AudioInputRendererHost // A map of stream IDs to audio sources. AudioEntryMap audio_entries_; + // Raw pointer of the UserInputMonitor. + media::UserInputMonitor* user_input_monitor_; + DISALLOW_COPY_AND_ASSIGN(AudioInputRendererHost); }; diff --git a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc index 572abf3e1c6..369e0a87f20 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc @@ -29,14 +29,17 @@ void AudioInputSyncWriter::UpdateRecordedBytes(uint32 bytes) { socket_->Send(&bytes, sizeof(bytes)); } -uint32 AudioInputSyncWriter::Write( - const void* data, uint32 size, double volume) { +uint32 AudioInputSyncWriter::Write(const void* data, + uint32 size, + double volume, + bool key_pressed) { uint8* ptr = static_cast<uint8*>(shared_memory_->memory()); ptr += current_segment_id_ * shared_memory_segment_size_; media::AudioInputBuffer* buffer = reinterpret_cast<media::AudioInputBuffer*>(ptr); buffer->params.volume = volume; buffer->params.size = size; + buffer->params.key_pressed = key_pressed; memcpy(buffer->audio, data, size); if (++current_segment_id_ >= shared_memory_segment_count_) diff --git a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h index 4cfe9e3f396..d16911f20c3 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h +++ b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h @@ -28,7 +28,10 @@ class AudioInputSyncWriter : public media::AudioInputController::SyncWriter { // media::AudioOutputController::SyncWriter implementation. virtual void UpdateRecordedBytes(uint32 bytes) OVERRIDE; - virtual uint32 Write(const void* data, uint32 size, double volume) OVERRIDE; + virtual uint32 Write(const void* data, + uint32 size, + double volume, + bool key_pressed) OVERRIDE; virtual void Close() OVERRIDE; bool Init(); diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host.cc b/chromium/content/browser/renderer_host/media/audio_renderer_host.cc index 53f2eb2ae90..c09dc6ce08b 100644 --- a/chromium/content/browser/renderer_host/media/audio_renderer_host.cc +++ b/chromium/content/browser/renderer_host/media/audio_renderer_host.cc @@ -36,6 +36,7 @@ class AudioRendererHost::AudioEntry int stream_id, int render_view_id, const media::AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, scoped_ptr<base::SharedMemory> shared_memory, scoped_ptr<media::AudioOutputController::SyncReader> reader); @@ -88,6 +89,7 @@ class AudioRendererHost::AudioEntry AudioRendererHost::AudioEntry::AudioEntry( AudioRendererHost* host, int stream_id, int render_view_id, const media::AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, scoped_ptr<base::SharedMemory> shared_memory, scoped_ptr<media::AudioOutputController::SyncReader> reader) @@ -95,7 +97,8 @@ AudioRendererHost::AudioEntry::AudioEntry( stream_id_(stream_id), render_view_id_(render_view_id), controller_(media::AudioOutputController::Create( - host->audio_manager_, this, params, input_device_id, reader.get())), + host->audio_manager_, this, params, output_device_id, + input_device_id, reader.get())), shared_memory_(shared_memory.Pass()), reader_(reader.Pass()) { DCHECK(controller_.get()); @@ -301,10 +304,16 @@ void AudioRendererHost::OnCreateStream( // When the |input_channels| is valid, clients are trying to create a unified // IO stream which opens an input device mapping to the |session_id|. - std::string input_device_id; + // Initialize the |output_device_id| to an empty string which indicates that + // the default device should be used. If a StreamDeviceInfo instance was found + // though, then we use the matched output device. + std::string input_device_id, output_device_id; + const StreamDeviceInfo* info = media_stream_manager_-> + audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); + if (info) + output_device_id = info->device.matched_output_device_id; + if (input_channels > 0) { - const StreamDeviceInfo* info = media_stream_manager_-> - audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); if (!info) { SendErrorMessage(stream_id); DLOG(WARNING) << "No permission has been granted to input stream with " @@ -346,16 +355,18 @@ void AudioRendererHost::OnCreateStream( media_observer->OnCreatingAudioStream(render_process_id_, render_view_id); scoped_ptr<AudioEntry> entry(new AudioEntry( - this, stream_id, render_view_id, params, input_device_id, - shared_memory.Pass(), + this, stream_id, render_view_id, params, output_device_id, + input_device_id, shared_memory.Pass(), reader.PassAs<media::AudioOutputController::SyncReader>())); if (mirroring_manager_) { mirroring_manager_->AddDiverter( render_process_id_, entry->render_view_id(), entry->controller()); } audio_entries_.insert(std::make_pair(stream_id, entry.release())); - if (media_internals_) - media_internals_->OnSetAudioStreamStatus(this, stream_id, "created"); + if (media_internals_) { + media_internals_->OnAudioStreamCreated( + this, stream_id, params, input_device_id); + } } void AudioRendererHost::OnPlayStream(int stream_id) { diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc index 42fecc07a96..26c0963e5e8 100644 --- a/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc @@ -207,8 +207,6 @@ class AudioRendererHostTest : public testing::Test { } void Create(bool unified_stream) { - EXPECT_CALL(*observer_, - OnSetAudioStreamStatus(_, kStreamId, "created")); EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _)) .WillOnce(DoAll(Assign(&is_stream_active_, true), QuitMessageLoop(message_loop_.get()))); diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device.cc b/chromium/content/browser/renderer_host/media/desktop_capture_device.cc index 5a03e465533..16e69bf8ad0 100644 --- a/chromium/content/browser/renderer_host/media/desktop_capture_device.cc +++ b/chromium/content/browser/renderer_host/media/desktop_capture_device.cc @@ -23,7 +23,19 @@ namespace content { namespace { + const int kBytesPerPixel = 4; + +webrtc::DesktopRect ComputeLetterboxRect( + const webrtc::DesktopSize& max_size, + const webrtc::DesktopSize& source_size) { + gfx::Rect result = media::ComputeLetterboxRegion( + gfx::Rect(0, 0, max_size.width(), max_size.height()), + gfx::Size(source_size.width(), source_size.height())); + return webrtc::DesktopRect::MakeLTRB( + result.x(), result.y(), result.right(), result.bottom()); +} + } // namespace class DesktopCaptureDevice::Core @@ -34,8 +46,7 @@ class DesktopCaptureDevice::Core scoped_ptr<webrtc::DesktopCapturer> capturer); // Implementation of VideoCaptureDevice methods. - void Allocate(int width, int height, - int frame_rate, + void Allocate(const media::VideoCaptureCapability& capture_format, EventHandler* event_handler); void Start(); void Stop(); @@ -51,11 +62,16 @@ class DesktopCaptureDevice::Core // Helper methods that run on the |task_runner_|. Posted from the // corresponding public methods. - void DoAllocate(int width, int height, int frame_rate); + void DoAllocate(const media::VideoCaptureCapability& capture_format); void DoStart(); void DoStop(); void DoDeAllocate(); + // Chooses new output properties based on the supplied source size and the + // properties requested to Allocate(), and dispatches OnFrameInfo[Changed] + // notifications. + void RefreshCaptureFormat(const webrtc::DesktopSize& frame_size); + // Helper to schedule capture tasks. void ScheduleCaptureTimer(); @@ -78,24 +94,23 @@ class DesktopCaptureDevice::Core base::Lock event_handler_lock_; EventHandler* event_handler_; - // Requested size specified to Allocate(). - webrtc::DesktopSize requested_size_; - - // Frame rate specified to Allocate(). - int frame_rate_; + // Requested video capture format (width, height, frame rate, etc). + media::VideoCaptureCapability requested_format_; - // Empty until the first frame has been captured, and the output dimensions - // chosen based on the capture frame's size, and any caller-supplied - // size constraints. - webrtc::DesktopSize output_size_; + // Actual video capture format being generated. + media::VideoCaptureCapability capture_format_; - // Size of the most recently received frame. + // Size of frame most recently captured from the source. webrtc::DesktopSize previous_frame_size_; - // DesktopFrame into which captured frames are scaled, if the source size does - // not match |output_size_|. If the source and output have the same dimensions - // then this is NULL. - scoped_ptr<webrtc::DesktopFrame> scaled_frame_; + // DesktopFrame into which captured frames are down-scaled and/or letterboxed, + // depending upon the caller's requested capture capabilities. If frames can + // be returned to the caller directly then this is NULL. + scoped_ptr<webrtc::DesktopFrame> output_frame_; + + // Sub-rectangle of |output_frame_| into which the source will be scaled + // and/or letterboxed. + webrtc::DesktopRect output_rect_; // True between DoStart() and DoStop(). Can't just check |event_handler_| // because |event_handler_| is used on the caller thread. @@ -125,12 +140,12 @@ DesktopCaptureDevice::Core::Core( DesktopCaptureDevice::Core::~Core() { } -void DesktopCaptureDevice::Core::Allocate(int width, int height, - int frame_rate, - EventHandler* event_handler) { - DCHECK_GT(width, 0); - DCHECK_GT(height, 0); - DCHECK_GT(frame_rate, 0); +void DesktopCaptureDevice::Core::Allocate( + const media::VideoCaptureCapability& capture_format, + EventHandler* event_handler) { + DCHECK_GT(capture_format.width, 0); + DCHECK_GT(capture_format.height, 0); + DCHECK_GT(capture_format.frame_rate, 0); { base::AutoLock auto_lock(event_handler_lock_); @@ -139,7 +154,7 @@ void DesktopCaptureDevice::Core::Allocate(int width, int height, task_runner_->PostTask( FROM_HERE, - base::Bind(&Core::DoAllocate, this, width, height, frame_rate)); + base::Bind(&Core::DoAllocate, this, capture_format)); } void DesktopCaptureDevice::Core::Start() { @@ -180,95 +195,76 @@ void DesktopCaptureDevice::Core::OnCaptureCompleted( scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); - // If an |output_size_| hasn't yet been chosen then choose one, based upon - // the source frame size and the requested size supplied to Allocate(). - if (output_size_.is_empty()) { - // Treat the requested size as upper bounds on width & height. - // TODO(wez): Constraints should be passed from getUserMedia to Allocate. - output_size_.set( - std::min(frame->size().width(), requested_size_.width()), - std::min(frame->size().height(), requested_size_.height())); - - // Inform the EventHandler of the output dimensions, format and frame rate. - media::VideoCaptureCapability caps; - caps.width = output_size_.width(); - caps.height = output_size_.height(); - caps.frame_rate = frame_rate_; - caps.color = media::VideoCaptureCapability::kARGB; - caps.expected_capture_delay = - base::Time::kMillisecondsPerSecond / frame_rate_; - caps.interlaced = false; - - base::AutoLock auto_lock(event_handler_lock_); - if (event_handler_) - event_handler_->OnFrameInfo(caps); - } + // Handle initial frame size and size changes. + RefreshCaptureFormat(frame->size()); if (!started_) return; - size_t output_bytes = output_size_.width() * output_size_.height() * + webrtc::DesktopSize output_size(capture_format_.width, + capture_format_.height); + size_t output_bytes = output_size.width() * output_size.height() * webrtc::DesktopFrame::kBytesPerPixel; + const uint8_t* output_data = NULL; - if (frame->size().equals(output_size_)) { + if (frame->size().equals(output_size)) { // If the captured frame matches the output size, we can return the pixel // data directly, without scaling. - scaled_frame_.reset(); - - base::AutoLock auto_lock(event_handler_lock_); - if (event_handler_) { - event_handler_->OnIncomingCapturedFrame( - frame->data(), output_bytes, base::Time::Now(), 0, false, false); + output_data = frame->data(); + } else { + // Otherwise we need to down-scale and/or letterbox to the target format. + + // Allocate a buffer of the correct size to scale the frame into. + // |output_frame_| is cleared whenever |output_rect_| changes, so we don't + // need to worry about clearing out stale pixel data in letterboxed areas. + if (!output_frame_) { + output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); + memset(output_frame_->data(), 0, output_bytes); } - return; - } - - // If the output size differs from the frame size (e.g. the source has changed - // from its original dimensions, or the caller specified size constraints) - // then we need to scale the image. - if (!scaled_frame_) - scaled_frame_.reset(new webrtc::BasicDesktopFrame(output_size_)); - DCHECK(scaled_frame_->size().equals(output_size_)); - - // If the source frame size changed then clear |scaled_frame_|'s pixels. - if (!previous_frame_size_.equals(frame->size())) { - previous_frame_size_ = frame->size(); - memset(scaled_frame_->data(), 0, output_bytes); + DCHECK(output_frame_->size().equals(output_size)); + + // TODO(wez): Optimize this to scale only changed portions of the output, + // using ARGBScaleClip(). + uint8_t* output_rect_data = output_frame_->data() + + output_frame_->stride() * output_rect_.top() + + webrtc::DesktopFrame::kBytesPerPixel * output_rect_.left(); + libyuv::ARGBScale(frame->data(), frame->stride(), + frame->size().width(), frame->size().height(), + output_rect_data, output_frame_->stride(), + output_rect_.width(), output_rect_.height(), + libyuv::kFilterBilinear); + output_data = output_frame_->data(); } - // Determine the output size preserving aspect, and center in output buffer. - gfx::Rect scaled_rect = media::ComputeLetterboxRegion( - gfx::Rect(0, 0, output_size_.width(), output_size_.height()), - gfx::Size(frame->size().width(), frame->size().height())); - uint8* scaled_data = scaled_frame_->data() + - scaled_frame_->stride() * scaled_rect.y() + - webrtc::DesktopFrame::kBytesPerPixel * scaled_rect.x(); - - // TODO(wez): Optimize this to scale only changed portions of the output, - // using ARGBScaleClip(). - libyuv::ARGBScale(frame->data(), frame->stride(), - frame->size().width(), frame->size().height(), - scaled_data, scaled_frame_->stride(), - scaled_rect.width(), scaled_rect.height(), - libyuv::kFilterBilinear); - base::AutoLock auto_lock(event_handler_lock_); if (event_handler_) { - event_handler_->OnIncomingCapturedFrame( - scaled_frame_->data(), output_bytes, - base::Time::Now(), 0, false, false); + event_handler_->OnIncomingCapturedFrame(output_data, output_bytes, + base::Time::Now(), 0, false, false); } } -void DesktopCaptureDevice::Core::DoAllocate(int width, - int height, - int frame_rate) { +void DesktopCaptureDevice::Core::DoAllocate( + const media::VideoCaptureCapability& capture_format) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); DCHECK(desktop_capturer_); - requested_size_.set(width, height); - output_size_.set(0, 0); - frame_rate_ = frame_rate; + requested_format_ = capture_format; + + // Store requested frame rate and calculate expected delay. + capture_format_.frame_rate = requested_format_.frame_rate; + capture_format_.expected_capture_delay = + base::Time::kMillisecondsPerSecond / requested_format_.frame_rate; + + // Support dynamic changes in resolution only if requester also does. + if (requested_format_.frame_size_type == + media::VariableResolutionVideoCaptureDevice) { + capture_format_.frame_size_type = + media::VariableResolutionVideoCaptureDevice; + } + + // This capturer always outputs ARGB, non-interlaced. + capture_format_.color = media::PIXEL_FORMAT_ARGB; + capture_format_.interlaced = false; desktop_capturer_->Start(this); @@ -288,14 +284,63 @@ void DesktopCaptureDevice::Core::DoStart() { void DesktopCaptureDevice::Core::DoStop() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); started_ = false; - scaled_frame_.reset(); + output_frame_.reset(); + previous_frame_size_.set(0, 0); } void DesktopCaptureDevice::Core::DoDeAllocate() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); DoStop(); desktop_capturer_.reset(); - output_size_.set(0, 0); +} + +void DesktopCaptureDevice::Core::RefreshCaptureFormat( + const webrtc::DesktopSize& frame_size) { + if (previous_frame_size_.equals(frame_size)) + return; + + // Clear the output frame, if any, since it will either need resizing, or + // clearing of stale data in letterbox areas, anyway. + output_frame_.reset(); + + if (previous_frame_size_.is_empty() || + requested_format_.frame_size_type == + media::VariableResolutionVideoCaptureDevice) { + // If this is the first frame, or the receiver supports variable resolution + // then determine the output size by treating the requested width & height + // as maxima. + if (frame_size.width() > requested_format_.width || + frame_size.height() > requested_format_.height) { + output_rect_ = ComputeLetterboxRect( + webrtc::DesktopSize(requested_format_.width, + requested_format_.height), + frame_size); + output_rect_.Translate(-output_rect_.left(), -output_rect_.top()); + } else { + output_rect_ = webrtc::DesktopRect::MakeSize(frame_size); + } + capture_format_.width = output_rect_.width(); + capture_format_.height = output_rect_.height(); + + { + base::AutoLock auto_lock(event_handler_lock_); + if (event_handler_) { + if (previous_frame_size_.is_empty()) { + event_handler_->OnFrameInfo(capture_format_); + } else { + event_handler_->OnFrameInfoChanged(capture_format_); + } + } + } + } else { + // Otherwise the output frame size cannot change, so just scale and + // letterbox. + output_rect_ = ComputeLetterboxRect( + webrtc::DesktopSize(capture_format_.width, capture_format_.height), + frame_size); + } + + previous_frame_size_ = frame_size; } void DesktopCaptureDevice::Core::ScheduleCaptureTimer() { @@ -303,7 +348,7 @@ void DesktopCaptureDevice::Core::ScheduleCaptureTimer() { capture_task_posted_ = true; task_runner_->PostDelayedTask( FROM_HERE, base::Bind(&Core::OnCaptureTimer, this), - base::TimeDelta::FromSeconds(1) / frame_rate_); + base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate); } void DesktopCaptureDevice::Core::OnCaptureTimer() { @@ -343,11 +388,10 @@ scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create( case DesktopMediaID::TYPE_SCREEN: { scoped_ptr<webrtc::DesktopCapturer> capturer; -#if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) && defined(USE_X11) +#if defined(OS_CHROMEOS) && defined(USE_X11) // ScreenCapturerX11 polls by default, due to poor driver support for // DAMAGE. ChromeOS' drivers [can be patched to] support DAMAGE properly, - // so use it. However ARM driver seems to not support this properly, so - // disable it for ARM. See http://crbug.com/230105 . + // so use it. capturer.reset(webrtc::ScreenCapturer::CreateWithXDamage(true)); #elif defined(OS_WIN) // ScreenCapturerWin disables Aero by default. We don't want it disabled @@ -394,11 +438,8 @@ DesktopCaptureDevice::~DesktopCaptureDevice() { void DesktopCaptureDevice::Allocate( const media::VideoCaptureCapability& capture_format, - EventHandler* observer) { - core_->Allocate(capture_format.width, - capture_format.height, - capture_format.frame_rate, - observer); + EventHandler* event_handler) { + core_->Allocate(capture_format, event_handler); } void DesktopCaptureDevice::Start() { diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device.h b/chromium/content/browser/renderer_host/media/desktop_capture_device.h index 84422d3c5c9..8f60c7de1b8 100644 --- a/chromium/content/browser/renderer_host/media/desktop_capture_device.h +++ b/chromium/content/browser/renderer_host/media/desktop_capture_device.h @@ -25,7 +25,7 @@ struct DesktopMediaID; // DesktopCaptureDevice implements VideoCaptureDevice for screens and windows. // It's essentially an adapter between webrtc::DesktopCapturer and // VideoCaptureDevice. -class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice { +class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice1 { public: // Creates capturer for the specified |source| and then creates // DesktopCaptureDevice for it. May return NULL in case of a failure (e.g. if diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc b/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc index 0f14585ea90..2b6479e3cb2 100644 --- a/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc +++ b/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc @@ -17,7 +17,9 @@ #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" using ::testing::_; +using ::testing::AnyNumber; using ::testing::DoAll; +using ::testing::Expectation; using ::testing::InvokeWithoutArgs; using ::testing::SaveArg; @@ -25,6 +27,10 @@ namespace content { namespace { +MATCHER_P2(EqualsCaptureCapability, width, height, "") { + return arg.width == width && arg.height == height; +} + const int kTestFrameWidth1 = 100; const int kTestFrameHeight1 = 100; const int kTestFrameWidth2 = 200; @@ -38,6 +44,8 @@ class MockFrameObserver : public media::VideoCaptureDevice::EventHandler { MOCK_METHOD0(ReserveOutputBuffer, scoped_refptr<media::VideoFrame>()); MOCK_METHOD0(OnError, void()); MOCK_METHOD1(OnFrameInfo, void(const media::VideoCaptureCapability& info)); + MOCK_METHOD1(OnFrameInfoChanged, + void(const media::VideoCaptureCapability& info)); MOCK_METHOD6(OnIncomingCapturedFrame, void(const uint8* data, int length, base::Time timestamp, @@ -124,7 +132,7 @@ TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) { InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); media::VideoCaptureCapability capture_format( - 640, 480, kFrameRate, media::VideoCaptureCapability::kI420, 0, false, + 640, 480, kFrameRate, media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); capture_device.Allocate(capture_format, &frame_observer); capture_device.Start(); @@ -135,14 +143,15 @@ TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) { EXPECT_GT(caps.width, 0); EXPECT_GT(caps.height, 0); EXPECT_EQ(kFrameRate, caps.frame_rate); - EXPECT_EQ(media::VideoCaptureCapability::kARGB, caps.color); + EXPECT_EQ(media::PIXEL_FORMAT_ARGB, caps.color); EXPECT_FALSE(caps.interlaced); EXPECT_EQ(caps.width * caps.height * 4, frame_size); } -// Test that screen capturer can handle resolution change without crashing. -TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChange) { +// Test that screen capturer behaves correctly if the source frame size changes +// but the caller cannot cope with variable resolution output. +TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) { FakeScreenCapturer* mock_capturer = new FakeScreenCapturer(); DesktopCaptureDevice capture_device( @@ -154,11 +163,14 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChange) { int frame_size; MockFrameObserver frame_observer; - EXPECT_CALL(frame_observer, OnFrameInfo(_)) + Expectation frame_info_called = EXPECT_CALL(frame_observer, OnFrameInfo(_)) .WillOnce(SaveArg<0>(&caps)); + EXPECT_CALL(frame_observer, OnFrameInfoChanged(_)) + .Times(0); EXPECT_CALL(frame_observer, OnError()) .Times(0); EXPECT_CALL(frame_observer, OnIncomingCapturedFrame(_, _, _, _, _, _)) + .After(frame_info_called) .WillRepeatedly(DoAll( SaveArg<1>(&frame_size), InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); @@ -167,27 +179,93 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChange) { kTestFrameWidth1, kTestFrameHeight1, kFrameRate, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); + capture_device.Allocate(capture_format, &frame_observer); capture_device.Start(); - // Capture first frame. + + // Capture at least two frames, to ensure that the source frame size has + // changed while capturing. EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); done_event.Reset(); - // Capture second frame. EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + capture_device.Stop(); capture_device.DeAllocate(); EXPECT_EQ(kTestFrameWidth1, caps.width); EXPECT_EQ(kTestFrameHeight1, caps.height); EXPECT_EQ(kFrameRate, caps.frame_rate); - EXPECT_EQ(media::VideoCaptureCapability::kARGB, caps.color); + EXPECT_EQ(media::PIXEL_FORMAT_ARGB, caps.color); EXPECT_FALSE(caps.interlaced); EXPECT_EQ(caps.width * caps.height * 4, frame_size); } +// Test that screen capturer behaves correctly if the source frame size changes +// and the caller can cope with variable resolution output. +TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) { + FakeScreenCapturer* mock_capturer = new FakeScreenCapturer(); + + DesktopCaptureDevice capture_device( + worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()), + scoped_ptr<webrtc::DesktopCapturer>(mock_capturer)); + + media::VideoCaptureCapability caps; + base::WaitableEvent done_event(false, false); + + MockFrameObserver frame_observer; + Expectation frame_info_called = EXPECT_CALL(frame_observer, OnFrameInfo(_)) + .WillOnce(SaveArg<0>(&caps)); + Expectation first_info_changed = EXPECT_CALL(frame_observer, + OnFrameInfoChanged(EqualsCaptureCapability(kTestFrameWidth2, + kTestFrameHeight2))) + .After(frame_info_called); + Expectation second_info_changed = EXPECT_CALL(frame_observer, + OnFrameInfoChanged(EqualsCaptureCapability(kTestFrameWidth1, + kTestFrameHeight1))) + .After(first_info_changed); + EXPECT_CALL(frame_observer, OnFrameInfoChanged(_)) + .Times(AnyNumber()) + .After(second_info_changed); + EXPECT_CALL(frame_observer, OnError()) + .Times(0); + EXPECT_CALL(frame_observer, OnIncomingCapturedFrame(_, _, _, _, _, _)) + .After(frame_info_called) + .WillRepeatedly( + InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)); + + media::VideoCaptureCapability capture_format( + kTestFrameWidth2, + kTestFrameHeight2, + kFrameRate, + media::PIXEL_FORMAT_I420, + 0, + false, + media::VariableResolutionVideoCaptureDevice); + + capture_device.Allocate(capture_format, &frame_observer); + capture_device.Start(); + + // Capture at least three frames, to ensure that the source frame size has + // changed at least twice while capturing. + EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + done_event.Reset(); + EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + done_event.Reset(); + EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + + capture_device.Stop(); + capture_device.DeAllocate(); + + EXPECT_EQ(kTestFrameWidth1, caps.width); + EXPECT_EQ(kTestFrameHeight1, caps.height); + EXPECT_EQ(kFrameRate, caps.frame_rate); + EXPECT_EQ(media::PIXEL_FORMAT_ARGB, caps.color); + EXPECT_FALSE(caps.interlaced); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter.cc b/chromium/content/browser/renderer_host/media/device_request_message_filter.cc index 770e800d402..96a5de2e194 100644 --- a/chromium/content/browser/renderer_host/media/device_request_message_filter.cc +++ b/chromium/content/browser/renderer_host/media/device_request_message_filter.cc @@ -2,7 +2,6 @@ // 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/media/device_request_message_filter.h" #include "base/strings/string_number_conversions.h" @@ -193,7 +192,13 @@ bool DeviceRequestMessageFilter::DoesRawIdMatchGuid( bool result = hmac.Init(security_origin.spec()); DCHECK(result); std::vector<uint8> converted_guid; - base::HexStringToBytes(device_guid, &converted_guid); + // |device_guid| is not guaranteed to be a hex string, so check for success. + bool success = base::HexStringToBytes(device_guid, &converted_guid); + + if (!success) + return false; + + DCHECK_GT(converted_guid.size(), 0u); return hmac.Verify( raw_device_id, base::StringPiece(reinterpret_cast<const char*>(&converted_guid[0]), diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter.h b/chromium/content/browser/renderer_host/media/device_request_message_filter.h index 048e2157871..b684954384c 100644 --- a/chromium/content/browser/renderer_host/media/device_request_message_filter.h +++ b/chromium/content/browser/renderer_host/media/device_request_message_filter.h @@ -2,8 +2,8 @@ // 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_MEDIA_MEDIA_STREAM_CENTER_HOST_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_CENTER_HOST_H_ +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_DEVICE_REQUEST_MESSAGE_FILTER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_DEVICE_REQUEST_MESSAGE_FILTER_H_ #include <map> #include <string> @@ -75,4 +75,4 @@ class CONTENT_EXPORT DeviceRequestMessageFilter : public BrowserMessageFilter, } // namespace content -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_CENTER_HOST_H_ +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_DEVICE_REQUEST_MESSAGE_FILTER_H_ diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc b/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc index 04a7bb69eea..0daad820084 100644 --- a/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc +++ b/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc @@ -301,4 +301,20 @@ TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicAllowCamera) { EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); } +TEST_F(DeviceRequestMessageFilterTest, TestRawIdMatchGuid_EmptyGuid) { + GURL origin("https://test.com"); + const std::string device_guid = ""; + const std::string raw_device_id = "device"; + EXPECT_FALSE(DeviceRequestMessageFilter::DoesRawIdMatchGuid( + origin, device_guid, raw_device_id)); +} + +TEST_F(DeviceRequestMessageFilterTest, TestRawIdMatchGuid_NonHexGuid) { + GURL origin("https://test.com"); + const std::string device_guid = "garbage"; + const std::string raw_device_id = "device"; + EXPECT_FALSE(DeviceRequestMessageFilter::DoesRawIdMatchGuid( + origin, device_guid, raw_device_id)); +} + }; // namespace content diff --git a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc index 05804266035..2b7d6836326 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc @@ -274,16 +274,6 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateThreeStreams) { EXPECT_EQ(host_->NumberOfStreams(), 0u); } -TEST_F(MediaStreamDispatcherHostTest, FailOpenVideoDevice) { - StreamOptions options(MEDIA_NO_SERVICE, MEDIA_DEVICE_VIDEO_CAPTURE); - - media::FakeVideoCaptureDevice::SetFailNextCreate(); - SetupFakeUI(false); - EXPECT_CALL(*host_.get(), - OnStreamGenerationFailed(kRenderId, kPageRequestId)); - GenerateStreamAndWaitForResult(kPageRequestId, options); -} - TEST_F(MediaStreamDispatcherHostTest, CancelPendingStreamsOnChannelClosing) { StreamOptions options(MEDIA_NO_SERVICE, MEDIA_DEVICE_VIDEO_CAPTURE); diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager.cc b/chromium/content/browser/renderer_host/media/media_stream_manager.cc index 449331198bd..4c234b66ab1 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_manager.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_manager.cc @@ -740,8 +740,8 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, audio_input_device_manager_->GetOpenedDeviceInfoById( device_it->session_id); DCHECK_EQ(info->device.id, device_it->device.id); - device_it->device.sample_rate = info->device.sample_rate; - device_it->device.channel_layout = info->device.channel_layout; + device_it->device.input = info->device.input; + device_it->device.matched_output = info->device.matched_output; } audio_devices.push_back(*device_it); } else if (IsVideoMediaType(device_it->device.type)) { @@ -949,8 +949,8 @@ void MediaStreamManager::HandleAccessRequestResponse( if (sample_rate <= 0 || sample_rate > 96000) sample_rate = 44100; - device_info.device.sample_rate = sample_rate; - device_info.device.channel_layout = media::CHANNEL_LAYOUT_STEREO; + device_info.device.input.sample_rate = sample_rate; + device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO; } } diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc index 91413710020..a67237a1ed9 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc @@ -50,6 +50,7 @@ class MockAudioManager : public AudioManagerPlatform { virtual void GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) OVERRIDE { + DCHECK(device_names->empty()); if (HasAudioInputDevices()) { AudioManagerBase::GetAudioInputDeviceNames(device_names); } else { diff --git a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc b/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc index 8ecbabcd47d..53f5a769313 100644 --- a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc +++ b/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc @@ -5,6 +5,7 @@ #include "content/browser/renderer_host/media/midi_dispatcher_host.h" #include "base/bind.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/media/midi_messages.h" #include "content/public/browser/browser_context.h" @@ -57,6 +58,8 @@ void MIDIDispatcherHost::OnRequestSysExPermission(int render_view_id, void MIDIDispatcherHost::WasSysExPermissionGranted(int render_view_id, int client_id, bool success) { + ChildProcessSecurityPolicyImpl::GetInstance()->GrantSendMIDISysExMessage( + render_process_id_); Send(new MIDIMsg_SysExPermissionApproved(render_view_id, client_id, success)); } diff --git a/chromium/content/browser/renderer_host/media/midi_host.cc b/chromium/content/browser/renderer_host/media/midi_host.cc index 6ed473afeff..0467404bda3 100644 --- a/chromium/content/browser/renderer_host/media/midi_host.cc +++ b/chromium/content/browser/renderer_host/media/midi_host.cc @@ -9,10 +9,12 @@ #include "base/debug/trace_event.h" #include "base/process/process.h" #include "content/browser/browser_main_loop.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/media/media_internals.h" #include "content/common/media/midi_messages.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/media_observer.h" +#include "content/public/browser/user_metrics.h" #include "media/midi/midi_manager.h" using media::MIDIManager; @@ -32,8 +34,9 @@ static const uint8 kSysExMessage = 0xf0; namespace content { -MIDIHost::MIDIHost(media::MIDIManager* midi_manager) - : midi_manager_(midi_manager), +MIDIHost::MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager) + : renderer_process_id_(renderer_process_id), + midi_manager_(midi_manager), sent_bytes_in_flight_(0), bytes_sent_since_last_acknowledgement_(0) { } @@ -88,12 +91,15 @@ void MIDIHost::OnStartSession(int client_id) { output_ports)); } -void MIDIHost::OnSendData(int port, +void MIDIHost::OnSendData(uint32 port, const std::vector<uint8>& data, double timestamp) { if (!midi_manager_) return; + if (data.empty()) + return; + base::AutoLock auto_lock(in_flight_lock_); // Sanity check that we won't send too much. @@ -102,47 +108,48 @@ void MIDIHost::OnSendData(int port, data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes) return; - // For now disallow all System Exclusive messages even if we - // have permission. - // TODO(toyoshim): allow System Exclusive if browser has granted - // this client access. We'll likely need to pass a GURL - // here to compare against our permissions. - if (data.size() > 0 && data[0] >= kSysExMessage) + if (data[0] >= kSysExMessage) { + // Blink running in a renderer checks permission to raise a SecurityError in + // JavaScript. The actual permission check for security perposes happens + // here in the browser process. + if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage( + renderer_process_id_)) { + RecordAction(UserMetricsAction("BadMessageTerminate_MIDI")); + BadMessageReceived(); return; + } + } -#if defined(OS_ANDROID) - // TODO(toyoshim): figure out why data() method does not compile on Android. - NOTIMPLEMENTED(); -#else midi_manager_->DispatchSendMIDIData( this, port, - data.data(), - data.size(), + data, timestamp); -#endif sent_bytes_in_flight_ += data.size(); } void MIDIHost::ReceiveMIDIData( - int port_index, + uint32 port, const uint8* data, size_t length, double timestamp) { TRACE_EVENT0("midi", "MIDIHost::ReceiveMIDIData"); - // For now disallow all System Exclusive messages even if we - // have permission. - // TODO(toyoshim): allow System Exclusive if browser has granted - // this client access. We'll likely need to pass a GURL - // here to compare against our permissions. - if (length > 0 && data[0] >= kSysExMessage) + // Check a process security policy to receive a system exclusive message. + if (length > 0 && data[0] >= kSysExMessage) { + if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage( + renderer_process_id_)) { + // MIDI devices may send a system exclusive messages even if the renderer + // doesn't have a permission to receive it. Don't kill the renderer as + // OnSendData() does. return; + } + } // Send to the renderer. std::vector<uint8> v(data, data + length); - Send(new MIDIMsg_DataReceived(port_index, v, timestamp)); + Send(new MIDIMsg_DataReceived(port, v, timestamp)); } void MIDIHost::AccumulateMIDIBytesSent(size_t n) { diff --git a/chromium/content/browser/renderer_host/media/midi_host.h b/chromium/content/browser/renderer_host/media/midi_host.h index f6b2813264e..944325d99a0 100644 --- a/chromium/content/browser/renderer_host/media/midi_host.h +++ b/chromium/content/browser/renderer_host/media/midi_host.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ +#include <vector> + #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" @@ -23,7 +25,7 @@ class CONTENT_EXPORT MIDIHost public media::MIDIManagerClient { public: // Called from UI thread from the owner of this object. - MIDIHost(media::MIDIManager* midi_manager); + MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager); // BrowserMessageFilter implementation. virtual void OnChannelClosing() OVERRIDE; @@ -33,7 +35,7 @@ class CONTENT_EXPORT MIDIHost // MIDIManagerClient implementation. virtual void ReceiveMIDIData( - int port_index, + uint32 port, const uint8* data, size_t length, double timestamp) OVERRIDE; @@ -43,7 +45,7 @@ class CONTENT_EXPORT MIDIHost void OnStartSession(int client_id); // Data to be sent to a MIDI output port. - void OnSendData(int port, + void OnSendData(uint32 port, const std::vector<uint8>& data, double timestamp); @@ -53,6 +55,8 @@ class CONTENT_EXPORT MIDIHost virtual ~MIDIHost(); + int renderer_process_id_; + // |midi_manager_| talks to the platform-specific MIDI APIs. // It can be NULL if the platform (or our current implementation) // does not support MIDI. If not supported then a call to diff --git a/chromium/content/browser/renderer_host/media/mock_media_observer.h b/chromium/content/browser/renderer_host/media/mock_media_observer.h index fc3734744ea..5f7fb847b3c 100644 --- a/chromium/content/browser/renderer_host/media/mock_media_observer.h +++ b/chromium/content/browser/renderer_host/media/mock_media_observer.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "content/browser/media/media_internals.h" #include "content/public/browser/media_observer.h" +#include "media/audio/audio_parameters.h" #include "media/base/media_log_event.h" #include "testing/gmock/include/gmock/gmock.h" @@ -47,6 +48,10 @@ class MockMediaInternals : public MediaInternals { void(void* host, int stream_id)); MOCK_METHOD3(OnSetAudioStreamPlaying, void(void* host, int stream_id, bool playing)); + MOCK_METHOD4(OnAudioStreamCreated, + void(void* host, int stream_id, + const media::AudioParameters& params, + const std::string& input_device_id)); MOCK_METHOD3(OnSetAudioStreamStatus, void(void* host, int stream_id, const std::string& status)); MOCK_METHOD3(OnSetAudioStreamVolume, diff --git a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc index 207f86a99f3..3e30834feb2 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc @@ -128,8 +128,12 @@ int VideoCaptureBufferPool::RecognizeReservedBuffer( scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame( const gfx::Size& size, int rotation) { - if (static_cast<size_t>(size.GetArea() * 3 / 2) != GetMemorySize()) + if (GetMemorySize() != + media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)) { + DCHECK_EQ(GetMemorySize(), + media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)); return NULL; + } base::AutoLock lock(lock_); @@ -151,6 +155,7 @@ scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame( gfx::Rect(size), size, static_cast<uint8*>(buffer->shared_memory.memory()), + GetMemorySize(), buffer->shared_memory.handle(), base::TimeDelta(), disposal_handler); diff --git a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc index 67a8be4c761..30509a34c75 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc @@ -21,8 +21,8 @@ TEST(VideoCaptureBufferPoolTest, BufferPool) { scoped_refptr<media::VideoFrame> non_pool_frame = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta()); - scoped_refptr<VideoCaptureBufferPool> pool = - new VideoCaptureBufferPool(size.GetArea() * 3 / 2, 3); + scoped_refptr<VideoCaptureBufferPool> pool = new VideoCaptureBufferPool( + media::VideoFrame::AllocationSize(media::VideoFrame::I420, size), 3); ASSERT_EQ(460800u, pool->GetMemorySize()); ASSERT_TRUE(pool->Allocate()); diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller.cc b/chromium/content/browser/renderer_host/media/video_capture_controller.cc index bac43289a21..687a21a4996 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_controller.cc @@ -7,6 +7,7 @@ #include <set> #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "content/browser/renderer_host/media/media_stream_manager.h" @@ -22,6 +23,7 @@ namespace { +#if defined(OS_IOS) || defined(OS_ANDROID) // TODO(wjia): Support stride. void RotatePackedYV12Frame( const uint8* src, @@ -43,6 +45,7 @@ void RotatePackedYV12Frame( media::RotatePlaneByPixels( src, dest_vplane, width/2, height/2, rotation, flip_vert, flip_horiz); } +#endif // #if defined(OS_IOS) || defined(OS_ANDROID) } // namespace @@ -77,31 +80,109 @@ struct VideoCaptureController::ControllerClient { // Buffers used by this client. std::set<int> buffers; - // State of capture session, controlled by VideoCaptureManager directly. + // State of capture session, controlled by VideoCaptureManager directly. This + // transitions to true as soon as StopSession() occurs, at which point the + // client is sent an OnEnded() event. However, because the client retains a + // VideoCaptureController* pointer, its ControllerClient entry lives on until + // it unregisters itself via RemoveClient(), which may happen asynchronously. + // + // TODO(nick): If we changed the semantics of VideoCaptureHost so that + // OnEnded() events were processed synchronously (with the RemoveClient() done + // implicitly), we could avoid tracking this state here in the Controller, and + // simplify the code in both places. bool session_closed; }; -VideoCaptureController::VideoCaptureController( - VideoCaptureManager* video_capture_manager) - : chopped_width_(0), - chopped_height_(0), - frame_info_available_(false), - video_capture_manager_(video_capture_manager), - device_in_use_(false), - state_(VIDEO_CAPTURE_STATE_STOPPED) { +// Receives events from the VideoCaptureDevice and posts them to a +// VideoCaptureController on the IO thread. An instance of this class may safely +// outlive its target VideoCaptureController. +// +// Methods of this class may be called from any thread, and in practice will +// often be called on some auxiliary thread depending on the platform and the +// device type; including, for example, the DirectShow thread on Windows, the +// v4l2_thread on Linux, and the UI thread for tab capture. +class VideoCaptureController::VideoCaptureDeviceClient + : public media::VideoCaptureDevice::EventHandler { + public: + explicit VideoCaptureDeviceClient( + const base::WeakPtr<VideoCaptureController>& controller); + virtual ~VideoCaptureDeviceClient(); + + // VideoCaptureDevice::EventHandler implementation. + virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE; + virtual void OnIncomingCapturedFrame(const uint8* data, + int length, + base::Time timestamp, + int rotation, + bool flip_vert, + bool flip_horiz) OVERRIDE; + virtual void OnIncomingCapturedVideoFrame( + const scoped_refptr<media::VideoFrame>& frame, + base::Time timestamp) OVERRIDE; + virtual void OnError() OVERRIDE; + virtual void OnFrameInfo( + const media::VideoCaptureCapability& info) OVERRIDE; + virtual void OnFrameInfoChanged( + const media::VideoCaptureCapability& info) OVERRIDE; + + private: + // The controller to which we post events. + const base::WeakPtr<VideoCaptureController> controller_; + + // The pool of shared-memory buffers used for capturing. + scoped_refptr<VideoCaptureBufferPool> buffer_pool_; + + // Chopped pixels in width/height in case video capture device has odd + // numbers for width/height. + int chopped_width_; + int chopped_height_; + + // Tracks the current frame format. + media::VideoCaptureCapability frame_info_; + + // For NV21 we have to do color conversion into the intermediate buffer and + // from there the rotations. This variable won't be needed after + // http://crbug.com/292400 +#if defined(OS_IOS) || defined(OS_ANDROID) + scoped_ptr<uint8[]> i420_intermediate_buffer_; +#endif // #if defined(OS_IOS) || defined(OS_ANDROID) +}; + +VideoCaptureController::VideoCaptureController() + : state_(VIDEO_CAPTURE_STATE_STARTED), + weak_ptr_factory_(this) { memset(¤t_params_, 0, sizeof(current_params_)); } -void VideoCaptureController::StartCapture( +VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient( + const base::WeakPtr<VideoCaptureController>& controller) + : controller_(controller), + chopped_width_(0), + chopped_height_(0) {} + +VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} + +base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +scoped_ptr<media::VideoCaptureDevice::EventHandler> +VideoCaptureController::NewDeviceClient() { + scoped_ptr<media::VideoCaptureDevice::EventHandler> result( + new VideoCaptureDeviceClient(this->GetWeakPtr())); + return result.Pass(); +} + +void VideoCaptureController::AddClient( const VideoCaptureControllerID& id, VideoCaptureControllerEventHandler* event_handler, base::ProcessHandle render_process, const media::VideoCaptureParams& params) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DVLOG(1) << "VideoCaptureController::StartCapture, id " << id.device_id + DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id << ", (" << params.width << ", " << params.height - << ", " << params.frame_per_second + << ", " << params.frame_rate << ", " << params.session_id << ")"; @@ -111,71 +192,32 @@ void VideoCaptureController::StartCapture( return; } - // Do nothing if this client has called StartCapture before. - if (FindClient(id, event_handler, controller_clients_) || - FindClient(id, event_handler, pending_clients_)) + // Do nothing if this client has called AddClient before. + if (FindClient(id, event_handler, controller_clients_)) return; ControllerClient* client = new ControllerClient(id, event_handler, render_process, params); - // In case capture has been started, need to check different conditions. + // If we already have gotten frame_info from the device, repeat it to the new + // client. if (state_ == VIDEO_CAPTURE_STATE_STARTED) { - // TODO(wjia): Temporarily disable restarting till client supports resampling. -#if 0 - // This client has higher resolution than what is currently requested. - // Need restart capturing. - if (params.width > current_params_.width || - params.height > current_params_.height) { - video_capture_manager_->Stop(current_params_.session_id, - base::Bind(&VideoCaptureController::OnDeviceStopped, this)); - frame_info_available_ = false; - state_ = VIDEO_CAPTURE_STATE_STOPPING; - pending_clients_.push_back(client); - return; - } -#endif - - // This client's resolution is no larger than what's currently requested. - // When frame_info has been returned by device, send them to client. - if (frame_info_available_) { + if (frame_info_.IsValid()) { SendFrameInfoAndBuffers(client); } controller_clients_.push_back(client); return; } - - // In case the device is in the middle of stopping, put the client in - // pending queue. - if (state_ == VIDEO_CAPTURE_STATE_STOPPING) { - pending_clients_.push_back(client); - return; - } - - // Fresh start. - controller_clients_.push_back(client); - current_params_ = params; - // Order the manager to start the actual capture. - video_capture_manager_->Start(params, this); - state_ = VIDEO_CAPTURE_STATE_STARTED; - device_in_use_ = true; } -void VideoCaptureController::StopCapture( +int VideoCaptureController::RemoveClient( const VideoCaptureControllerID& id, VideoCaptureControllerEventHandler* event_handler) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DVLOG(1) << "VideoCaptureController::StopCapture, id " << id.device_id; + DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id; - ControllerClient* client = FindClient(id, event_handler, pending_clients_); - // If the client is still in pending queue, just remove it. - if (client) { - pending_clients_.remove(client); - return; - } - - client = FindClient(id, event_handler, controller_clients_); + ControllerClient* client = FindClient(id, event_handler, controller_clients_); if (!client) - return; + return kInvalidMediaCaptureSessionId; // Take back all buffers held by the |client|. if (buffer_pool_.get()) { @@ -189,28 +231,17 @@ void VideoCaptureController::StopCapture( client->buffers.clear(); int session_id = client->parameters.session_id; - delete client; controller_clients_.remove(client); + delete client; - // No more clients. Stop device. - if (controller_clients_.empty() && - (state_ == VIDEO_CAPTURE_STATE_STARTED || - state_ == VIDEO_CAPTURE_STATE_ERROR)) { - video_capture_manager_->Stop(session_id, - base::Bind(&VideoCaptureController::OnDeviceStopped, this)); - frame_info_available_ = false; - state_ = VIDEO_CAPTURE_STATE_STOPPING; - } + return session_id; } -void VideoCaptureController::StopSession( - int session_id) { +void VideoCaptureController::StopSession(int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id; - ControllerClient* client = FindClient(session_id, pending_clients_); - if (!client) - client = FindClient(session_id, controller_clients_); + ControllerClient* client = FindClient(session_id, controller_clients_); if (client) { client->session_closed = true; @@ -224,59 +255,174 @@ void VideoCaptureController::ReturnBuffer( int buffer_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ControllerClient* client = FindClient(id, event_handler, - controller_clients_); + ControllerClient* client = FindClient(id, event_handler, controller_clients_); // If this buffer is not held by this client, or this client doesn't exist // in controller, do nothing. if (!client || - client->buffers.find(buffer_id) == client->buffers.end()) + client->buffers.find(buffer_id) == client->buffers.end()) { + NOTREACHED(); return; + } client->buffers.erase(buffer_id); buffer_pool_->RelinquishConsumerHold(buffer_id, 1); - - // When all buffers have been returned by clients and device has been - // called to stop, check if restart is needed. This could happen when - // capture needs to be restarted due to resolution change. - if (!buffer_pool_->IsAnyBufferHeldForConsumers() && - state_ == VIDEO_CAPTURE_STATE_STOPPING) { - PostStopping(); - } } -scoped_refptr<media::VideoFrame> VideoCaptureController::ReserveOutputBuffer() { - base::AutoLock lock(buffer_pool_lock_); - if (!buffer_pool_.get()) - return NULL; +scoped_refptr<media::VideoFrame> +VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer() { return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, frame_info_.height), 0); } -// Implements VideoCaptureDevice::EventHandler. -// OnIncomingCapturedFrame is called the thread running the capture device. -// I.e.- DirectShow thread on windows and v4l2_thread on Linux. -void VideoCaptureController::OnIncomingCapturedFrame( +#if !defined(OS_IOS) && !defined(OS_ANDROID) +void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( const uint8* data, int length, base::Time timestamp, int rotation, bool flip_vert, bool flip_horiz) { - DCHECK(frame_info_.color == media::VideoCaptureCapability::kI420 || - frame_info_.color == media::VideoCaptureCapability::kYV12 || - (rotation == 0 && !flip_vert && !flip_horiz)); + TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); - scoped_refptr<media::VideoFrame> dst; - { - base::AutoLock lock(buffer_pool_lock_); - if (!buffer_pool_.get()) - return; - dst = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, - frame_info_.height), - rotation); + if (!buffer_pool_.get()) + return; + scoped_refptr<media::VideoFrame> dst = buffer_pool_->ReserveI420VideoFrame( + gfx::Size(frame_info_.width, frame_info_.height), rotation); + + if (!dst.get()) + return; + + uint8* yplane = dst->data(media::VideoFrame::kYPlane); + uint8* uplane = dst->data(media::VideoFrame::kUPlane); + uint8* vplane = dst->data(media::VideoFrame::kVPlane); + int yplane_stride = frame_info_.width; + int uv_plane_stride = (frame_info_.width + 1) / 2; + int crop_x = 0; + int crop_y = 0; + libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY; + // Assuming rotation happens first and flips next, we can consolidate both + // vertical and horizontal flips together with rotation into two variables: + // new_rotation = (rotation + 180 * vertical_flip) modulo 360 + // new_vertical_flip = horizontal_flip XOR vertical_flip + int new_rotation_angle = (rotation + 180 * flip_vert) % 360; + libyuv::RotationMode rotation_mode = libyuv::kRotate0; + if (new_rotation_angle == 90) + rotation_mode = libyuv::kRotate90; + else if (new_rotation_angle == 180) + rotation_mode = libyuv::kRotate180; + else if (new_rotation_angle == 270) + rotation_mode = libyuv::kRotate270; + + switch (frame_info_.color) { + case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. + break; + case media::PIXEL_FORMAT_I420: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_I420; + break; + case media::PIXEL_FORMAT_YV12: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_YV12; + break; + case media::PIXEL_FORMAT_NV21: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_NV12; + break; + case media::PIXEL_FORMAT_YUY2: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_YUY2; + break; + case media::PIXEL_FORMAT_UYVY: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_UYVY; + break; + case media::PIXEL_FORMAT_RGB24: + origin_colorspace = libyuv::FOURCC_RAW; + break; + case media::PIXEL_FORMAT_ARGB: + origin_colorspace = libyuv::FOURCC_ARGB; + break; + case media::PIXEL_FORMAT_MJPEG: + origin_colorspace = libyuv::FOURCC_MJPG; + break; + default: + NOTREACHED(); + } + + int need_convert_rgb24_on_win = false; +#if defined(OS_WIN) + // kRGB24 on Windows start at the bottom line and has a negative stride. This + // is not supported by libyuv, so the media API is used instead. + if (frame_info_.color == media::PIXEL_FORMAT_RGB24) { + // Rotation and flipping is not supported in kRGB24 and OS_WIN case. + DCHECK(!rotation && !flip_vert && !flip_horiz); + need_convert_rgb24_on_win = true; } +#endif + if (need_convert_rgb24_on_win) { + int rgb_stride = -3 * (frame_info_.width + chopped_width_); + const uint8* rgb_src = + data + 3 * (frame_info_.width + chopped_width_) * + (frame_info_.height - 1 + chopped_height_); + media::ConvertRGB24ToYUV(rgb_src, + yplane, + uplane, + vplane, + frame_info_.width, + frame_info_.height, + rgb_stride, + yplane_stride, + uv_plane_stride); + } else { + libyuv::ConvertToI420( + data, + length, + yplane, + yplane_stride, + uplane, + uv_plane_stride, + vplane, + uv_plane_stride, + crop_x, + crop_y, + frame_info_.width + chopped_width_, + frame_info_.height * (flip_vert ^ flip_horiz ? -1 : 1), + frame_info_.width, + frame_info_.height, + rotation_mode, + origin_colorspace); + } + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, + controller_, + dst, + timestamp)); +} +#else +void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( + const uint8* data, + int length, + base::Time timestamp, + int rotation, + bool flip_vert, + bool flip_horiz) { + DCHECK(frame_info_.color == media::PIXEL_FORMAT_I420 || + frame_info_.color == media::PIXEL_FORMAT_YV12 || + frame_info_.color == media::PIXEL_FORMAT_NV21 || + (rotation == 0 && !flip_vert && !flip_horiz)); + + TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); + + if (!buffer_pool_) + return; + scoped_refptr<media::VideoFrame> dst = + buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, + frame_info_.height), + rotation); if (!dst.get()) return; @@ -287,26 +433,36 @@ void VideoCaptureController::OnIncomingCapturedFrame( // Do color conversion from the camera format to I420. switch (frame_info_.color) { - case media::VideoCaptureCapability::kColorUnknown: // Color format not set. + case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. break; - case media::VideoCaptureCapability::kI420: + case media::PIXEL_FORMAT_I420: DCHECK(!chopped_width_ && !chopped_height_); RotatePackedYV12Frame( data, yplane, uplane, vplane, frame_info_.width, frame_info_.height, rotation, flip_vert, flip_horiz); break; - case media::VideoCaptureCapability::kYV12: + case media::PIXEL_FORMAT_YV12: DCHECK(!chopped_width_ && !chopped_height_); RotatePackedYV12Frame( data, yplane, vplane, uplane, frame_info_.width, frame_info_.height, rotation, flip_vert, flip_horiz); break; - case media::VideoCaptureCapability::kNV21: + case media::PIXEL_FORMAT_NV21: { DCHECK(!chopped_width_ && !chopped_height_); - media::ConvertNV21ToYUV(data, yplane, uplane, vplane, frame_info_.width, + int num_pixels = frame_info_.width * frame_info_.height; + media::ConvertNV21ToYUV(data, + &i420_intermediate_buffer_[0], + &i420_intermediate_buffer_[num_pixels], + &i420_intermediate_buffer_[num_pixels * 5 / 4], + frame_info_.width, frame_info_.height); - break; - case media::VideoCaptureCapability::kYUY2: + RotatePackedYV12Frame( + i420_intermediate_buffer_.get(), yplane, uplane, vplane, + frame_info_.width, frame_info_.height, + rotation, flip_vert, flip_horiz); + break; + } + case media::PIXEL_FORMAT_YUY2: DCHECK(!chopped_width_ && !chopped_height_); if (frame_info_.width * frame_info_.height * 2 != length) { // If |length| of |data| does not match the expected width and height @@ -317,42 +473,22 @@ void VideoCaptureController::OnIncomingCapturedFrame( media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width, frame_info_.height); break; - case media::VideoCaptureCapability::kRGB24: { + case media::PIXEL_FORMAT_RGB24: { int ystride = frame_info_.width; int uvstride = frame_info_.width / 2; -#if defined(OS_WIN) // RGB on Windows start at the bottom line. - int rgb_stride = -3 * (frame_info_.width + chopped_width_); - const uint8* rgb_src = data + 3 * (frame_info_.width + chopped_width_) * - (frame_info_.height -1 + chopped_height_); -#else int rgb_stride = 3 * (frame_info_.width + chopped_width_); const uint8* rgb_src = data; -#endif media::ConvertRGB24ToYUV(rgb_src, yplane, uplane, vplane, frame_info_.width, frame_info_.height, rgb_stride, ystride, uvstride); break; } - case media::VideoCaptureCapability::kARGB: + case media::PIXEL_FORMAT_ARGB: media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width, frame_info_.height, (frame_info_.width + chopped_width_) * 4, frame_info_.width, frame_info_.width / 2); break; -#if !defined(OS_IOS) && !defined(OS_ANDROID) - case media::VideoCaptureCapability::kMJPEG: { - int yplane_stride = frame_info_.width; - int uv_plane_stride = (frame_info_.width + 1) / 2; - int crop_x = 0; - int crop_y = 0; - libyuv::ConvertToI420(data, length, yplane, yplane_stride, uplane, - uv_plane_stride, vplane, uv_plane_stride, crop_x, - crop_y, frame_info_.width, frame_info_.height, - frame_info_.width, frame_info_.height, - libyuv::kRotate0, libyuv::FOURCC_MJPG); - break; - } -#endif default: NOTREACHED(); } @@ -360,38 +496,35 @@ void VideoCaptureController::OnIncomingCapturedFrame( BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, - this, dst, timestamp)); + controller_, dst, timestamp)); } +#endif // #if !defined(OS_IOS) && !defined(OS_ANDROID) -// OnIncomingCapturedVideoFrame is called the thread running the capture device. -void VideoCaptureController::OnIncomingCapturedVideoFrame( +void +VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( const scoped_refptr<media::VideoFrame>& frame, base::Time timestamp) { + if (!buffer_pool_) + return; - scoped_refptr<media::VideoFrame> target; - { - base::AutoLock lock(buffer_pool_lock_); - - if (!buffer_pool_.get()) - return; - - // If this is a frame that belongs to the buffer pool, we can forward it - // directly to the IO thread and be done. - if (buffer_pool_->RecognizeReservedBuffer( - frame->shared_memory_handle()) >= 0) { - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, - this, frame, timestamp)); - return; - } - // Otherwise, this is a frame that belongs to the caller, and we must copy - // it to a frame from the buffer pool. - target = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, - frame_info_.height), - 0); + // If this is a frame that belongs to the buffer pool, we can forward it + // directly to the IO thread and be done. + if (buffer_pool_->RecognizeReservedBuffer( + frame->shared_memory_handle()) >= 0) { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, + controller_, frame, timestamp)); + return; } + // Otherwise, this is a frame that belongs to the caller, and we must copy + // it to a frame from the buffer pool. + scoped_refptr<media::VideoFrame> target = + buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, + frame_info_.height), + 0); + if (!target.get()) return; @@ -478,18 +611,18 @@ void VideoCaptureController::OnIncomingCapturedVideoFrame( BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, - this, target, timestamp)); + controller_, target, timestamp)); } -void VideoCaptureController::OnError() { +void VideoCaptureController::VideoCaptureDeviceClient::OnError() { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::DoErrorOnIOThread, this)); + base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); } -void VideoCaptureController::OnFrameInfo( +void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfo( const media::VideoCaptureCapability& info) { - frame_info_= info; + frame_info_ = info; // Handle cases when |info| has odd numbers for width/height. if (info.width & 1) { --frame_info_.width; @@ -503,32 +636,51 @@ void VideoCaptureController::OnFrameInfo( } else { chopped_height_ = 0; } +#if defined(OS_IOS) || defined(OS_ANDROID) + if (frame_info_.color == media::PIXEL_FORMAT_NV21 && + !i420_intermediate_buffer_) { + i420_intermediate_buffer_.reset( + new uint8[frame_info_.width * frame_info_.height * 12 / 8]); + } +#endif // #if defined(OS_IOS) || defined(OS_ANDROID) + + DCHECK(!buffer_pool_.get()); + + // TODO(nick): Give BufferPool the same lifetime as the controller, have it + // support frame size changes, and stop checking it for NULL everywhere. + // http://crbug.com/266082 + buffer_pool_ = new VideoCaptureBufferPool( + media::VideoFrame::AllocationSize( + media::VideoFrame::I420, + gfx::Size(frame_info_.width, frame_info_.height)), + kNoOfBuffers); + + // Check whether all buffers were created successfully. + if (!buffer_pool_->Allocate()) { + // Transition to the error state. + buffer_pool_ = NULL; + OnError(); + return; + } + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, this)); + base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, controller_, + frame_info_, buffer_pool_)); } -void VideoCaptureController::OnFrameInfoChanged( +void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfoChanged( const media::VideoCaptureCapability& info) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureController::DoFrameInfoChangedOnIOThread, - this, info)); + controller_, info)); } VideoCaptureController::~VideoCaptureController() { buffer_pool_ = NULL; // Release all buffers. STLDeleteContainerPointers(controller_clients_.begin(), controller_clients_.end()); - STLDeleteContainerPointers(pending_clients_.begin(), - pending_clients_.end()); -} - -// Called by VideoCaptureManager when a device have been stopped. -void VideoCaptureController::OnDeviceStopped() { - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureController::DoDeviceStoppedOnIOThread, this)); } void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( @@ -563,37 +715,24 @@ void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( buffer_pool_->HoldForConsumers(buffer_id, count); } -void VideoCaptureController::DoFrameInfoOnIOThread() { +void VideoCaptureController::DoFrameInfoOnIOThread( + const media::VideoCaptureCapability& frame_info, + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!buffer_pool_.get()) - << "Device is restarted without releasing shared memory."; + DCHECK(!buffer_pool_.get()) << "Frame info should happen only once."; // Allocate memory only when device has been started. if (state_ != VIDEO_CAPTURE_STATE_STARTED) return; - scoped_refptr<VideoCaptureBufferPool> buffer_pool = - new VideoCaptureBufferPool(frame_info_.width * frame_info_.height * 3 / 2, - kNoOfBuffers); - - // Check whether all buffers were created successfully. - if (!buffer_pool->Allocate()) { - state_ = VIDEO_CAPTURE_STATE_ERROR; - for (ControllerClients::iterator client_it = controller_clients_.begin(); - client_it != controller_clients_.end(); ++client_it) { - (*client_it)->event_handler->OnError((*client_it)->controller_id); - } - return; - } - - { - base::AutoLock lock(buffer_pool_lock_); - buffer_pool_ = buffer_pool; - } - frame_info_available_ = true; + frame_info_ = frame_info; + buffer_pool_ = buffer_pool; for (ControllerClients::iterator client_it = controller_clients_.begin(); client_it != controller_clients_.end(); ++client_it) { + if ((*client_it)->session_closed) + continue; + SendFrameInfoAndBuffers(*client_it); } } @@ -605,6 +744,9 @@ void VideoCaptureController::DoFrameInfoChangedOnIOThread( // needed, to support the new video capture format. See crbug.com/266082. for (ControllerClients::iterator client_it = controller_clients_.begin(); client_it != controller_clients_.end(); ++client_it) { + if ((*client_it)->session_closed) + continue; + (*client_it)->event_handler->OnFrameInfoChanged( (*client_it)->controller_id, info.width, @@ -619,25 +761,16 @@ void VideoCaptureController::DoErrorOnIOThread() { ControllerClients::iterator client_it; for (client_it = controller_clients_.begin(); client_it != controller_clients_.end(); ++client_it) { - (*client_it)->event_handler->OnError((*client_it)->controller_id); - } - for (client_it = pending_clients_.begin(); - client_it != pending_clients_.end(); ++client_it) { - (*client_it)->event_handler->OnError((*client_it)->controller_id); - } -} + if ((*client_it)->session_closed) + continue; -void VideoCaptureController::DoDeviceStoppedOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - device_in_use_ = false; - if (state_ == VIDEO_CAPTURE_STATE_STOPPING) { - PostStopping(); + (*client_it)->event_handler->OnError((*client_it)->controller_id); } } void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(frame_info_available_); + DCHECK(frame_info_.IsValid()); client->event_handler->OnFrameInfo(client->controller_id, frame_info_); for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) { @@ -679,54 +812,9 @@ VideoCaptureController::FindClient( return NULL; } -// This function is called when all buffers have been returned to controller, -// or when device is stopped. It decides whether the device needs to be -// restarted. -void VideoCaptureController::PostStopping() { +int VideoCaptureController::GetClientCount() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPING); - - // When clients still have some buffers, or device has not been stopped yet, - // do nothing. - if ((buffer_pool_.get() && buffer_pool_->IsAnyBufferHeldForConsumers()) || - device_in_use_) - return; - - { - base::AutoLock lock(buffer_pool_lock_); - buffer_pool_ = NULL; - } - - // No more client. Therefore the controller is stopped. - if (controller_clients_.empty() && pending_clients_.empty()) { - state_ = VIDEO_CAPTURE_STATE_STOPPED; - return; - } - - // Restart the device. - current_params_.width = 0; - current_params_.height = 0; - ControllerClients::iterator client_it; - for (client_it = controller_clients_.begin(); - client_it != controller_clients_.end(); ++client_it) { - if (current_params_.width < (*client_it)->parameters.width) - current_params_.width = (*client_it)->parameters.width; - if (current_params_.height < (*client_it)->parameters.height) - current_params_.height = (*client_it)->parameters.height; - } - for (client_it = pending_clients_.begin(); - client_it != pending_clients_.end(); ) { - if (current_params_.width < (*client_it)->parameters.width) - current_params_.width = (*client_it)->parameters.width; - if (current_params_.height < (*client_it)->parameters.height) - current_params_.height = (*client_it)->parameters.height; - controller_clients_.push_back((*client_it)); - pending_clients_.erase(client_it++); - } - // Request the manager to start the actual capture. - video_capture_manager_->Start(current_params_, this); - state_ = VIDEO_CAPTURE_STATE_STARTED; - device_in_use_ = true; + return controller_clients_.size(); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller.h b/chromium/content/browser/renderer_host/media/video_capture_controller.h index 5d33d01163c..eda4ce31e01 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller.h +++ b/chromium/content/browser/renderer_host/media/video_capture_controller.h @@ -1,17 +1,48 @@ // Copyright (c) 2012 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. - -// VideoCaptureController is the glue between VideoCaptureHost, -// VideoCaptureManager and VideoCaptureDevice. -// It provides functions for VideoCaptureHost to start a VideoCaptureDevice and -// is responsible for keeping track of shared DIBs and filling them with I420 -// video frames for IPC communication between VideoCaptureHost and -// VideoCaptureMessageFilter. -// It implements media::VideoCaptureDevice::EventHandler to get video frames -// from a VideoCaptureDevice object and do color conversion straight into the -// shared DIBs to avoid a memory copy. -// It serves multiple VideoCaptureControllerEventHandlers. +// +// VideoCaptureController is the glue between a VideoCaptureDevice and all +// VideoCaptureHosts that have connected to it. A controller exists on behalf of +// one (and only one) VideoCaptureDevice; both are owned by the +// VideoCaptureManager. +// +// The VideoCaptureController is responsible for: +// +// * Allocating and keeping track of shared memory buffers, and filling them +// with I420 video frames for IPC communication between VideoCaptureHost (in +// the browser process) and VideoCaptureMessageFilter (in the renderer +// process). +// * Broadcasting the events from a single VideoCaptureDevice, fanning them +// out to multiple clients. +// * Keeping track of the clients on behalf of the VideoCaptureManager, making +// it possible for the Manager to delete the Controller and its Device when +// there are no clients left. +// +// A helper class, VCC::VideoCaptureDeviceClient, is responsible for: +// +// * Conveying events from the device thread (where VideoCaptureDevices live) +// the IO thread (where the VideoCaptureController lives). +// * Performing some image transformations on the output of the Device; +// specifically, colorspace conversion and rotation. +// +// Interactions between VideoCaptureController and other classes: +// +// * VideoCaptureController indirectly observes a VideoCaptureDevice +// by means of its proxy, VideoCaptureDeviceClient, which implements +// the VideoCaptureDevice::EventHandler interface. The proxy forwards +// observed events to the VideoCaptureController on the IO thread. +// * A VideoCaptureController interacts with its clients (VideoCaptureHosts) +// via the VideoCaptureControllerEventHandler interface. +// * Conversely, a VideoCaptureControllerEventHandler (typically, +// VideoCaptureHost) will interact directly with VideoCaptureController to +// return leased buffers by means of the ReturnBuffer() public method of +// VCC. +// * VideoCaptureManager (which owns the VCC) interacts directly with +// VideoCaptureController through its public methods, to add and remove +// clients. +// +// VideoCaptureController is not thread safe and operates on the IO thread only. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_ @@ -21,6 +52,8 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/process/process.h" #include "base/synchronization/lock.h" #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" @@ -32,31 +65,37 @@ #include "media/video/capture/video_capture_types.h" namespace content { -class VideoCaptureManager; class VideoCaptureBufferPool; -class CONTENT_EXPORT VideoCaptureController - : public base::RefCountedThreadSafe<VideoCaptureController>, - public media::VideoCaptureDevice::EventHandler { +class CONTENT_EXPORT VideoCaptureController { public: - VideoCaptureController(VideoCaptureManager* video_capture_manager); + VideoCaptureController(); + virtual ~VideoCaptureController(); + + base::WeakPtr<VideoCaptureController> GetWeakPtr(); + + // Return a new VideoCaptureDeviceClient to forward capture events to this + // instance. + scoped_ptr<media::VideoCaptureDevice::EventHandler> NewDeviceClient(); // Start video capturing and try to use the resolution specified in // |params|. - // When capturing has started, the |event_handler| receives a call OnFrameInfo - // with resolution that best matches the requested that the video - // capture device support. - void StartCapture(const VideoCaptureControllerID& id, - VideoCaptureControllerEventHandler* event_handler, - base::ProcessHandle render_process, - const media::VideoCaptureParams& params); - - // Stop video capture. - // This will take back all buffers held by by |event_handler|, and - // |event_handler| shouldn't use those buffers any more. - void StopCapture(const VideoCaptureControllerID& id, + // When capturing starts, the |event_handler| will receive an OnFrameInfo() + // call informing it of the resolution that was actually picked by the device. + void AddClient(const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + base::ProcessHandle render_process, + const media::VideoCaptureParams& params); + + // Stop video capture. This will take back all buffers held by by + // |event_handler|, and |event_handler| shouldn't use those buffers any more. + // Returns the session_id of the stopped client, or + // kInvalidMediaCaptureSessionId if the indicated client was not registered. + int RemoveClient(const VideoCaptureControllerID& id, VideoCaptureControllerEventHandler* event_handler); + int GetClientCount(); + // API called directly by VideoCaptureManager in case the device is // prematurely closed. void StopSession(int session_id); @@ -67,39 +106,19 @@ class CONTENT_EXPORT VideoCaptureController VideoCaptureControllerEventHandler* event_handler, int buffer_id); - // Implement media::VideoCaptureDevice::EventHandler. - virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE; - virtual void OnIncomingCapturedFrame(const uint8* data, - int length, - base::Time timestamp, - int rotation, - bool flip_vert, - bool flip_horiz) OVERRIDE; - virtual void OnIncomingCapturedVideoFrame( - const scoped_refptr<media::VideoFrame>& frame, - base::Time timestamp) OVERRIDE; - virtual void OnError() OVERRIDE; - virtual void OnFrameInfo(const media::VideoCaptureCapability& info) OVERRIDE; - virtual void OnFrameInfoChanged( - const media::VideoCaptureCapability& info) OVERRIDE; - - protected: - virtual ~VideoCaptureController(); - private: - friend class base::RefCountedThreadSafe<VideoCaptureController>; + class VideoCaptureDeviceClient; struct ControllerClient; typedef std::list<ControllerClient*> ControllerClients; - // Callback when manager has stopped device. - void OnDeviceStopped(); - - // Worker functions on IO thread. + // Worker functions on IO thread. Called by the VideoCaptureDeviceClient. void DoIncomingCapturedFrameOnIOThread( const scoped_refptr<media::VideoFrame>& captured_frame, base::Time timestamp); - void DoFrameInfoOnIOThread(); + void DoFrameInfoOnIOThread( + const media::VideoCaptureCapability& frame_info, + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool); void DoFrameInfoChangedOnIOThread(const media::VideoCaptureCapability& info); void DoErrorOnIOThread(); void DoDeviceStoppedOnIOThread(); @@ -118,45 +137,25 @@ class CONTENT_EXPORT VideoCaptureController int session_id, const ControllerClients& clients); - // Decide what to do after kStopping state. Dependent on events, controller - // can stay in kStopping state, or go to kStopped, or restart capture. - void PostStopping(); - - // Protects access to the |buffer_pool_| pointer on non-IO threads. IO thread - // must hold this lock when modifying the |buffer_pool_| pointer itself. - // TODO(nick): Make it so that this lock isn't required. - base::Lock buffer_pool_lock_; - // The pool of shared-memory buffers used for capturing. scoped_refptr<VideoCaptureBufferPool> buffer_pool_; // All clients served by this controller. ControllerClients controller_clients_; - // All clients waiting for service. - ControllerClients pending_clients_; - // The parameter that currently used for the capturing. media::VideoCaptureParams current_params_; - // It's modified on caller thread, assuming there is only one OnFrameInfo() - // call per StartCapture(). + // Tracks the current frame format. media::VideoCaptureCapability frame_info_; - // Chopped pixels in width/height in case video capture device has odd numbers - // for width/height. - int chopped_width_; - int chopped_height_; - - // It's accessed only on IO thread. - bool frame_info_available_; - - VideoCaptureManager* video_capture_manager_; - - bool device_in_use_; + // Takes on only the states 'STARTED' and 'ERROR'. 'ERROR' is an absorbing + // state which stops the flow of data to clients. VideoCaptureState state_; - DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureController); + base::WeakPtrFactory<VideoCaptureController> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(VideoCaptureController); }; } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h b/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h index c4844af2f73..4a2c294b0ba 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h +++ b/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h @@ -10,7 +10,7 @@ #include "content/common/content_export.h" namespace media { -struct VideoCaptureCapability; +class VideoCaptureCapability; } namespace content { diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc index c4b716d2e33..fec0942dde0 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc @@ -7,141 +7,74 @@ #include <string> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" -#include "content/browser/browser_thread_impl.h" +#include "base/run_loop.h" #include "content/browser/renderer_host/media/media_stream_provider.h" #include "content/browser/renderer_host/media/video_capture_controller.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/common/media/media_stream_options.h" -#include "media/video/capture/fake_video_capture_device.h" -#include "media/video/capture/video_capture_device.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "media/video/capture/video_capture_types.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::AtLeast; using ::testing::InSequence; -using ::testing::Return; +using ::testing::Mock; namespace content { -enum { kDeviceId = 1 }; - -ACTION_P4(StopCapture, controller, controller_id, controller_handler, - message_loop) { - message_loop->PostTask(FROM_HERE, - base::Bind(&VideoCaptureController::StopCapture, - controller, controller_id, controller_handler)); - message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); -} - -ACTION_P3(StopSession, controller, session_id, message_loop) { - message_loop->PostTask(FROM_HERE, - base::Bind(&VideoCaptureController::StopSession, - controller, session_id)); - message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); -} - class MockVideoCaptureControllerEventHandler : public VideoCaptureControllerEventHandler { public: - MockVideoCaptureControllerEventHandler(VideoCaptureController* controller, - base::MessageLoop* message_loop) - : controller_(controller), - message_loop_(message_loop), - controller_id_(kDeviceId), - process_handle_(base::kNullProcessHandle) { - } + explicit MockVideoCaptureControllerEventHandler( + VideoCaptureController* controller) + : controller_(controller) {} virtual ~MockVideoCaptureControllerEventHandler() {} + // These mock methods are delegated to by our fake implementation of + // VideoCaptureControllerEventHandler, to be used in EXPECT_CALL(). MOCK_METHOD1(DoBufferCreated, void(const VideoCaptureControllerID&)); MOCK_METHOD1(DoBufferReady, void(const VideoCaptureControllerID&)); MOCK_METHOD1(DoFrameInfo, void(const VideoCaptureControllerID&)); MOCK_METHOD1(DoEnded, void(const VideoCaptureControllerID&)); + MOCK_METHOD1(DoError, void(const VideoCaptureControllerID&)); - virtual void OnError(const VideoCaptureControllerID& id) OVERRIDE {} + virtual void OnError(const VideoCaptureControllerID& id) OVERRIDE { + DoError(id); + } virtual void OnBufferCreated(const VideoCaptureControllerID& id, base::SharedMemoryHandle handle, int length, int buffer_id) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoBufferCreated(id); } virtual void OnBufferReady(const VideoCaptureControllerID& id, int buffer_id, base::Time timestamp) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoBufferReady(id); - message_loop_->PostTask(FROM_HERE, + base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&VideoCaptureController::ReturnBuffer, - controller_, controller_id_, this, buffer_id)); + base::Unretained(controller_), id, this, buffer_id)); } virtual void OnFrameInfo( const VideoCaptureControllerID& id, const media::VideoCaptureCapability& format) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoFrameInfo(id); } virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoEnded(id); + // OnEnded() must respond by (eventually) unregistering the client. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(base::IgnoreResult(&VideoCaptureController::RemoveClient), + base::Unretained(controller_), id, this)); } - scoped_refptr<VideoCaptureController> controller_; - base::MessageLoop* message_loop_; - VideoCaptureControllerID controller_id_; - base::ProcessHandle process_handle_; -}; - -class MockVideoCaptureManager : public VideoCaptureManager { - public: - MockVideoCaptureManager() - : video_session_id_(kStartOpenSessionId), - device_name_("fake_device_0", "/dev/video0") {} - - void Init() { - video_capture_device_.reset( - media::FakeVideoCaptureDevice::Create(device_name_)); - ASSERT_TRUE(video_capture_device_.get() != NULL); - } - - MOCK_METHOD3(StartCapture, void(int, int, - media::VideoCaptureDevice::EventHandler*)); - MOCK_METHOD1(StopCapture, void(const media::VideoCaptureSessionId&)); - - void Start(const media::VideoCaptureParams& capture_params, - media::VideoCaptureDevice::EventHandler* vc_receiver) OVERRIDE { - StartCapture(capture_params.width, capture_params.height, vc_receiver); - // TODO(mcasas): Add testing for variable resolution video capture devices, - // supported by FakeVideoCaptureDevice. See crbug.com/261410, second part. - media::VideoCaptureCapability capture_format( - capture_params.width, - capture_params.height, - capture_params.frame_per_second, - media::VideoCaptureCapability::kI420, - 0, - false, - media::ConstantResolutionVideoCaptureDevice); - video_capture_device_->Allocate(capture_format, vc_receiver); - video_capture_device_->Start(); - } - - void Stop(const media::VideoCaptureSessionId& capture_session_id, - base::Closure stopped_cb) OVERRIDE { - StopCapture(capture_session_id); - video_capture_device_->Stop(); - video_capture_device_->DeAllocate(); - } - - int video_session_id_; - media::VideoCaptureDevice::Name device_name_; - scoped_ptr<media::VideoCaptureDevice> video_capture_device_; - - private: - virtual ~MockVideoCaptureManager() {} - DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureManager); + VideoCaptureController* controller_; }; // Test class. @@ -151,115 +84,371 @@ class VideoCaptureControllerTest : public testing::Test { virtual ~VideoCaptureControllerTest() {} protected: + static const int kPoolSize = 3; + virtual void SetUp() OVERRIDE { - message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO)); - file_thread_.reset(new BrowserThreadImpl(BrowserThread::FILE, - message_loop_.get())); - io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO, - message_loop_.get())); - - vcm_ = new MockVideoCaptureManager(); - vcm_->Init(); - controller_ = new VideoCaptureController(vcm_.get()); - controller_handler_.reset(new MockVideoCaptureControllerEventHandler( - controller_.get(), message_loop_.get())); + controller_.reset(new VideoCaptureController()); + device_ = controller_->NewDeviceClient().Pass(); + client_a_.reset(new MockVideoCaptureControllerEventHandler( + controller_.get())); + client_b_.reset(new MockVideoCaptureControllerEventHandler( + controller_.get())); } - virtual void TearDown() OVERRIDE {} + virtual void TearDown() OVERRIDE { + base::RunLoop().RunUntilIdle(); + } - scoped_ptr<base::MessageLoop> message_loop_; - scoped_ptr<BrowserThreadImpl> file_thread_; - scoped_ptr<BrowserThreadImpl> io_thread_; - scoped_refptr<MockVideoCaptureManager> vcm_; - scoped_ptr<MockVideoCaptureControllerEventHandler> controller_handler_; - scoped_refptr<VideoCaptureController> controller_; + TestBrowserThreadBundle bindle_; + scoped_ptr<MockVideoCaptureControllerEventHandler> client_a_; + scoped_ptr<MockVideoCaptureControllerEventHandler> client_b_; + scoped_ptr<VideoCaptureController> controller_; + scoped_ptr<media::VideoCaptureDevice::EventHandler> device_; private: DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest); }; -// Try to start and stop capture. -TEST_F(VideoCaptureControllerTest, StartAndStop) { - media::VideoCaptureParams capture_params; - capture_params.session_id = vcm_->video_session_id_; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - - InSequence s; - EXPECT_CALL(*vcm_.get(), - StartCapture(capture_params.width, - capture_params.height, - controller_.get())).Times(1); - EXPECT_CALL(*controller_handler_, - DoFrameInfo(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferCreated(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferReady(controller_handler_->controller_id_)) - .Times(AtLeast(1)) - .WillOnce(StopCapture(controller_.get(), - controller_handler_->controller_id_, - controller_handler_.get(), - message_loop_.get())); - EXPECT_CALL(*vcm_.get(), StopCapture(vcm_->video_session_id_)).Times(1); - - controller_->StartCapture(controller_handler_->controller_id_, - controller_handler_.get(), - controller_handler_->process_handle_, - capture_params); - message_loop_->Run(); +// A simple test of VideoCaptureController's ability to add, remove, and keep +// track of clients. +TEST_F(VideoCaptureControllerTest, AddAndRemoveClients) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + media::VideoCaptureParams session_300 = session_100; + session_300.session_id = 300; + + media::VideoCaptureParams session_400 = session_100; + session_400.session_id = 400; + + // Intentionally use the same route ID for two of the clients: the device_ids + // are a per-VideoCaptureHost namespace, and can overlap across hosts. + const VideoCaptureControllerID client_a_route_1(44); + const VideoCaptureControllerID client_a_route_2(30); + const VideoCaptureControllerID client_b_route_1(30); + const VideoCaptureControllerID client_b_route_2(1); + + // Clients in controller: [] + ASSERT_EQ(0, controller_->GetClientCount()) + << "Client count should initially be zero."; + controller_->AddClient(client_a_route_1, client_a_.get(), + base::kNullProcessHandle, session_100); + // Clients in controller: [A/1] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Adding client A/1 should bump client count.";; + controller_->AddClient(client_a_route_2, client_a_.get(), + base::kNullProcessHandle, session_200); + // Clients in controller: [A/1, A/2] + ASSERT_EQ(2, controller_->GetClientCount()) + << "Adding client A/2 should bump client count."; + controller_->AddClient(client_b_route_1, client_b_.get(), + base::kNullProcessHandle, session_300); + // Clients in controller: [A/1, A/2, B/1] + ASSERT_EQ(3, controller_->GetClientCount()) + << "Adding client B/1 should bump client count."; + ASSERT_EQ(200, + controller_->RemoveClient(client_a_route_2, client_a_.get())) + << "Removing client A/1 should return its session_id."; + // Clients in controller: [A/1, B/1] + ASSERT_EQ(2, controller_->GetClientCount()); + ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId), + controller_->RemoveClient(client_a_route_2, client_a_.get())) + << "Removing a nonexistant client should fail."; + // Clients in controller: [A/1, B/1] + ASSERT_EQ(2, controller_->GetClientCount()); + ASSERT_EQ(300, + controller_->RemoveClient(client_b_route_1, client_b_.get())) + << "Removing client B/1 should return its session_id."; + // Clients in controller: [A/1] + ASSERT_EQ(1, controller_->GetClientCount()); + controller_->AddClient(client_b_route_2, client_b_.get(), + base::kNullProcessHandle, session_400); + // Clients in controller: [A/1, B/2] + + EXPECT_CALL(*client_a_, DoEnded(client_a_route_1)).Times(1); + controller_->StopSession(100); // Session 100 == client A/1 + Mock::VerifyAndClearExpectations(client_a_.get()); + ASSERT_EQ(2, controller_->GetClientCount()) + << "Client should be closed but still exist after StopSession."; + // Clients in controller: [A/1 (closed, removal pending), B/2] + base::RunLoop().RunUntilIdle(); + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Client A/1 should be deleted by now."; + controller_->StopSession(200); // Session 200 does not exist anymore + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Stopping non-existant session 200 should be a no-op."; + controller_->StopSession(256); // Session 256 never existed. + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Stopping non-existant session 256 should be a no-op."; + ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId), + controller_->RemoveClient(client_a_route_1, client_a_.get())) + << "Removing already-removed client A/1 should fail."; + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Removing non-existant session 200 should be a no-op."; + ASSERT_EQ(400, + controller_->RemoveClient(client_b_route_2, client_b_.get())) + << "Removing client B/2 should return its session_id."; + // Clients in controller: [] + ASSERT_EQ(0, controller_->GetClientCount()) + << "Client count should return to zero after all clients are gone."; +} + +// This test will connect and disconnect several clients while simulating an +// active capture device being started and generating frames. It runs on one +// thread and is intended to behave deterministically. +TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + media::VideoCaptureParams session_300 = session_100; + session_300.session_id = 300; + + // session_id of 1 is kStartOpenSessionId, which should have special meaning + // to VideoCaptureManager, but not to VideoCaptureController ... so test it. + media::VideoCaptureParams session_1 = session_100; + session_1.session_id = VideoCaptureManager::kStartOpenSessionId; + + // The device format needn't match the VideoCaptureParams (the camera can do + // what it wants). Pick something random to use for OnFrameInfo. + media::VideoCaptureCapability device_format( + 10, 10, 25, media::PIXEL_FORMAT_RGB24, 10, false, + media::ConstantResolutionVideoCaptureDevice); + + const VideoCaptureControllerID client_a_route_1(0xa1a1a1a1); + const VideoCaptureControllerID client_a_route_2(0xa2a2a2a2); + const VideoCaptureControllerID client_b_route_1(0xb1b1b1b1); + const VideoCaptureControllerID client_b_route_2(0xb2b2b2b2); + + // Start with two clients. + controller_->AddClient(client_a_route_1, client_a_.get(), + base::kNullProcessHandle, session_100); + controller_->AddClient(client_b_route_1, client_b_.get(), + base::kNullProcessHandle, session_300); + ASSERT_EQ(2, controller_->GetClientCount()); + + // The OnFrameInfo() event from the device, when processed by the controller, + // should generate client OnFrameInfo() and OnBufferCreated() events. + { + InSequence s; + EXPECT_CALL(*client_a_, DoFrameInfo(client_a_route_1)).Times(1); + EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(kPoolSize); + } + { + InSequence s; + EXPECT_CALL(*client_b_, DoFrameInfo(client_b_route_1)).Times(1); + EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(kPoolSize); + } + device_->OnFrameInfo(device_format); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // When a third clients is subsequently added, the frame info and buffers + // should immediately be shared to the new clients. + { + InSequence s; + EXPECT_CALL(*client_a_, DoFrameInfo(client_a_route_2)).Times(1); + EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(kPoolSize); + } + controller_->AddClient(client_a_route_2, client_a_.get(), + base::kNullProcessHandle, session_200); + Mock::VerifyAndClearExpectations(client_a_.get()); + + // Now, simulate an incoming captured frame from the capture device. + uint8 frame_no = 1; + scoped_refptr<media::VideoFrame> frame; + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + frame = NULL; + + // The buffer should be delivered to the clients in any order. + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // Second frame. In this case pretend that the VideoFrame pointer is held + // by the device for a long delay. This shouldn't affect anything. + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + + // The buffer should be delivered to the clients in any order. + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + frame = NULL; + + // Add a fourth client now that some frames have come through. It should get + // the buffer info, but it won't get any frames until new ones are captured. + { + InSequence s; + EXPECT_CALL(*client_b_, DoFrameInfo(client_b_route_2)).Times(1); + EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_2)).Times(kPoolSize); + } + controller_->AddClient(client_b_route_2, client_b_.get(), + base::kNullProcessHandle, session_1); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // Third, fourth, and fifth frames. Pretend they all arrive at the same time. + for (int i = 0; i < kPoolSize; i++) { + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + ASSERT_EQ(media::VideoFrame::I420, frame->format()); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + + } + // ReserveOutputBuffer ought to fail now, because the pool is depleted. + ASSERT_FALSE(device_->ReserveOutputBuffer()); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(kPoolSize); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(kPoolSize); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(kPoolSize); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(kPoolSize); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // Now test the interaction of client shutdown and frame delivery. + // Kill A1 via renderer disconnect (synchronous). + controller_->RemoveClient(client_a_route_1, client_a_.get()); + // Kill B1 via session close (posts a task to disconnect). + EXPECT_CALL(*client_b_, DoEnded(client_b_route_1)).Times(1); + controller_->StopSession(300); + // Queue up another frame. + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + frame = device_->ReserveOutputBuffer(); + { + // Kill A2 via session close (posts a task to disconnect, but A2 must not + // be sent either of these two frames).. + EXPECT_CALL(*client_a_, DoEnded(client_a_route_2)).Times(1); + controller_->StopSession(200); + } + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + // B2 is the only client left, and is the only one that should + // get the frame. + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(2); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); +} + +// Exercises the OnError() codepath of VideoCaptureController, and tests the +// behavior of various operations after the error state has been signalled. +TEST_F(VideoCaptureControllerTest, ErrorBeforeDeviceCreation) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + const VideoCaptureControllerID route_id(0x99); + + // Start with one client. + controller_->AddClient(route_id, client_a_.get(), + base::kNullProcessHandle, session_100); + device_->OnError(); + EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + // Second client connects after the error state. It also should get told of + // the error. + EXPECT_CALL(*client_b_, DoError(route_id)).Times(1); + controller_->AddClient(route_id, client_b_.get(), + base::kNullProcessHandle, session_200); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // OnFrameInfo from the VCD should become a no-op after the error occurs. + media::VideoCaptureCapability device_format( + 10, 10, 25, media::PIXEL_FORMAT_ARGB, 10, false, + media::ConstantResolutionVideoCaptureDevice); + + device_->OnFrameInfo(device_format); + base::RunLoop().RunUntilIdle(); } -// Try to stop session before stopping capture. -TEST_F(VideoCaptureControllerTest, StopSession) { - media::VideoCaptureParams capture_params; - capture_params.session_id = vcm_->video_session_id_; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - - InSequence s; - EXPECT_CALL(*vcm_.get(), - StartCapture(capture_params.width, - capture_params.height, - controller_.get())).Times(1); - EXPECT_CALL(*controller_handler_, - DoFrameInfo(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferCreated(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferReady(controller_handler_->controller_id_)) - .Times(AtLeast(1)) - .WillOnce(StopSession(controller_.get(), - vcm_->video_session_id_, - message_loop_.get())); - EXPECT_CALL(*controller_handler_, - DoEnded(controller_handler_->controller_id_)) - .Times(1); - - controller_->StartCapture(controller_handler_->controller_id_, - controller_handler_.get(), - controller_handler_->process_handle_, - capture_params); - message_loop_->Run(); - - // The session is stopped now. There should be no buffer coming from - // controller. - EXPECT_CALL(*controller_handler_, - DoBufferReady(controller_handler_->controller_id_)) - .Times(0); - message_loop_->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), base::TimeDelta::FromSeconds(1)); - message_loop_->Run(); - - EXPECT_CALL(*vcm_.get(), StopCapture(vcm_->video_session_id_)).Times(1); - controller_->StopCapture(controller_handler_->controller_id_, - controller_handler_.get()); +// Exercises the OnError() codepath of VideoCaptureController, and tests the +// behavior of various operations after the error state has been signalled. +TEST_F(VideoCaptureControllerTest, ErrorAfterDeviceCreation) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + const VideoCaptureControllerID route_id(0x99); + + // Start with one client. + controller_->AddClient(route_id, client_a_.get(), + base::kNullProcessHandle, session_100); + // OnFrameInfo from the VCD should become a no-op after the error occurs. + media::VideoCaptureCapability device_format( + 10, 10, 25, media::PIXEL_FORMAT_ARGB, 10, false, + media::ConstantResolutionVideoCaptureDevice); + + // Start the device and get as far as exchanging buffers with the subprocess. + // Then, signal an error and deliver the frame. The error should be propagated + // to clients; the frame should not be. + device_->OnFrameInfo(device_format); + EXPECT_CALL(*client_a_, DoFrameInfo(route_id)).Times(1); + EXPECT_CALL(*client_a_, DoBufferCreated(route_id)).Times(kPoolSize); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + scoped_refptr<media::VideoFrame> frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + + device_->OnError(); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + frame = NULL; + + EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + // Second client connects after the error state. It also should get told of + // the error. + EXPECT_CALL(*client_b_, DoError(route_id)).Times(1); + controller_->AddClient(route_id, client_b_.get(), + base::kNullProcessHandle, session_200); + Mock::VerifyAndClearExpectations(client_b_.get()); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_host.cc b/chromium/content/browser/renderer_host/media/video_capture_host.cc index bc7c8c19d7d..7ed77ae53d3 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_host.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_host.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" -#include "base/stl_util.h" #include "content/browser/browser_main_loop.h" #include "content/browser/renderer_host/media/media_stream_manager.h" #include "content/browser/renderer_host/media/video_capture_manager.h" @@ -14,15 +13,6 @@ namespace content { -struct VideoCaptureHost::Entry { - Entry(VideoCaptureController* controller) - : controller(controller) {} - - ~Entry() {} - - scoped_refptr<VideoCaptureController> controller; -}; - VideoCaptureHost::VideoCaptureHost(MediaStreamManager* media_stream_manager) : media_stream_manager_(media_stream_manager) { } @@ -32,17 +22,15 @@ VideoCaptureHost::~VideoCaptureHost() {} void VideoCaptureHost::OnChannelClosing() { BrowserMessageFilter::OnChannelClosing(); - // Since the IPC channel is gone, close all requested VideCaptureDevices. + // Since the IPC channel is gone, close all requested VideoCaptureDevices. for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); it++) { - VideoCaptureController* controller = it->second->controller.get(); + const base::WeakPtr<VideoCaptureController>& controller = it->second; if (controller) { VideoCaptureControllerID controller_id(it->first); - controller->StopCapture(controller_id, this); - media_stream_manager_->video_capture_manager()->RemoveController( - controller, this); + media_stream_manager_->video_capture_manager()->StopCaptureForClient( + controller.get(), controller_id, this); } } - STLDeleteValues(&entries_); } void VideoCaptureHost::OnDestruct() const { @@ -95,11 +83,11 @@ void VideoCaptureHost::OnFrameInfoChanged( const VideoCaptureControllerID& controller_id, int width, int height, - int frame_per_second) { + int frame_rate) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureHost::DoSendFrameInfoChangedOnIOThread, - this, controller_id, width, height, frame_per_second)); + this, controller_id, width, height, frame_rate)); } void VideoCaptureHost::OnEnded(const VideoCaptureControllerID& controller_id) { @@ -170,7 +158,7 @@ void VideoCaptureHost::DoSendFrameInfoOnIOThread( media::VideoCaptureParams params; params.width = format.width; params.height = format.height; - params.frame_per_second = format.frame_rate; + params.frame_rate = format.frame_rate; params.frame_size_type = format.frame_size_type; Send(new VideoCaptureMsg_DeviceInfo(controller_id.device_id, params)); Send(new VideoCaptureMsg_StateChanged(controller_id.device_id, @@ -181,7 +169,7 @@ void VideoCaptureHost::DoSendFrameInfoChangedOnIOThread( const VideoCaptureControllerID& controller_id, int width, int height, - int frame_per_second) { + int frame_rate) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (entries_.find(controller_id) == entries_.end()) @@ -190,7 +178,7 @@ void VideoCaptureHost::DoSendFrameInfoChangedOnIOThread( media::VideoCaptureParams params; params.width = width; params.height = height; - params.frame_per_second = frame_per_second; + params.frame_rate = frame_rate; Send(new VideoCaptureMsg_DeviceInfoChanged(controller_id.device_id, params)); } @@ -215,7 +203,7 @@ void VideoCaptureHost::OnStartCapture(int device_id, DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << "VideoCaptureHost::OnStartCapture, device_id " << device_id << ", (" << params.width << ", " << params.height << ", " - << params.frame_per_second << ", " << params.session_id + << params.frame_rate << ", " << params.session_id << ", variable resolution device:" << ((params.frame_size_type == media::VariableResolutionVideoCaptureDevice) ? "yes" : "no") @@ -223,45 +211,44 @@ void VideoCaptureHost::OnStartCapture(int device_id, VideoCaptureControllerID controller_id(device_id); DCHECK(entries_.find(controller_id) == entries_.end()); - entries_[controller_id] = new Entry(NULL); - media_stream_manager_->video_capture_manager()->AddController( - params, this, base::Bind(&VideoCaptureHost::OnControllerAdded, this, - device_id, params)); + entries_[controller_id] = base::WeakPtr<VideoCaptureController>(); + media_stream_manager_->video_capture_manager()->StartCaptureForClient( + params, PeerHandle(), controller_id, this, base::Bind( + &VideoCaptureHost::OnControllerAdded, this, device_id, params)); } void VideoCaptureHost::OnControllerAdded( int device_id, const media::VideoCaptureParams& params, - VideoCaptureController* controller) { + const base::WeakPtr<VideoCaptureController>& controller) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureHost::DoControllerAddedOnIOThread, - this, device_id, params, make_scoped_refptr(controller))); + this, device_id, params, controller)); } void VideoCaptureHost::DoControllerAddedOnIOThread( int device_id, const media::VideoCaptureParams params, - VideoCaptureController* controller) { + const base::WeakPtr<VideoCaptureController>& controller) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); VideoCaptureControllerID controller_id(device_id); EntryMap::iterator it = entries_.find(controller_id); if (it == entries_.end()) { if (controller) { - media_stream_manager_->video_capture_manager()->RemoveController( - controller, this); + media_stream_manager_->video_capture_manager()->StopCaptureForClient( + controller.get(), controller_id, this); } return; } - if (controller == NULL) { + if (!controller) { Send(new VideoCaptureMsg_StateChanged(device_id, VIDEO_CAPTURE_STATE_ERROR)); - delete it->second; entries_.erase(controller_id); return; } - it->second->controller = controller; - controller->StartCapture(controller_id, this, PeerHandle(), params); + DCHECK(!it->second); + it->second = controller; } void VideoCaptureHost::OnStopCapture(int device_id) { @@ -288,8 +275,8 @@ void VideoCaptureHost::OnReceiveEmptyBuffer(int device_id, int buffer_id) { VideoCaptureControllerID controller_id(device_id); EntryMap::iterator it = entries_.find(controller_id); if (it != entries_.end()) { - scoped_refptr<VideoCaptureController> controller = it->second->controller; - if (controller.get()) + const base::WeakPtr<VideoCaptureController>& controller = it->second; + if (controller) controller->ReturnBuffer(controller_id, this, buffer_id); } } @@ -302,14 +289,11 @@ void VideoCaptureHost::DeleteVideoCaptureControllerOnIOThread( if (it == entries_.end()) return; - VideoCaptureController* controller = it->second->controller.get(); - if (controller) { - controller->StopCapture(controller_id, this); - media_stream_manager_->video_capture_manager()->RemoveController( - controller, this); + if (it->second) { + media_stream_manager_->video_capture_manager()->StopCaptureForClient( + it->second.get(), controller_id, this); } - delete it->second; - entries_.erase(controller_id); + entries_.erase(it); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_host.h b/chromium/content/browser/renderer_host/media/video_capture_host.h index 025b849de94..6ce395631eb 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_host.h +++ b/chromium/content/browser/renderer_host/media/video_capture_host.h @@ -39,6 +39,7 @@ #include <map> #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner_helpers.h" #include "content/browser/renderer_host/media/video_capture_controller.h" #include "content/common/content_export.h" @@ -46,7 +47,7 @@ #include "ipc/ipc_message.h" namespace media { -struct VideoCaptureCapability; +class VideoCaptureCapability; } namespace content { @@ -97,10 +98,10 @@ class CONTENT_EXPORT VideoCaptureHost const media::VideoCaptureParams& params); void OnControllerAdded( int device_id, const media::VideoCaptureParams& params, - VideoCaptureController* controller); + const base::WeakPtr<VideoCaptureController>& controller); void DoControllerAddedOnIOThread( int device_id, const media::VideoCaptureParams params, - VideoCaptureController* controller); + const base::WeakPtr<VideoCaptureController>& controller); // IPC message: Stop capture on device referenced by |device_id|. void OnStopCapture(int device_id); @@ -148,9 +149,12 @@ class CONTENT_EXPORT VideoCaptureHost MediaStreamManager* media_stream_manager_; - struct Entry; - typedef std::map<VideoCaptureControllerID, Entry*> EntryMap; - // A map of VideoCaptureControllerID to its state and VideoCaptureController. + typedef std::map<VideoCaptureControllerID, + base::WeakPtr<VideoCaptureController> > EntryMap; + + // A map of VideoCaptureControllerID to the VideoCaptureController to which it + // is connected. An entry in this map holds a null controller while it is in + // the process of starting. EntryMap entries_; DISALLOW_COPY_AND_ASSIGN(VideoCaptureHost); diff --git a/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc index 762148c86a2..ca1891dc426 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc @@ -20,6 +20,7 @@ #include "content/public/test/mock_resource_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "media/audio/audio_manager.h" +#include "media/base/video_frame.h" #include "media/video/capture/video_capture_types.h" #include "net/url_request/url_request_context.h" #include "testing/gmock/include/gmock/gmock.h" @@ -36,10 +37,10 @@ using ::testing::Return; namespace content { // Id used to identify the capture session between renderer and -// video_capture_host. -static const int kDeviceId = 1; +// video_capture_host. This is an arbitrary value. +static const int kDeviceId = 555; // Id of a video capture device -static const media::VideoCaptureSessionId kTestFakeDeviceId = +static const media::VideoCaptureSessionId kTestFakeSessionId = VideoCaptureManager::kStartOpenSessionId; // Define to enable test where video is dumped to file. @@ -57,7 +58,8 @@ class DumpVideo { base::FilePath file_name = base::FilePath(base::StringPrintf( FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height)); file_.reset(file_util::OpenFile(file_name, "wb")); - expected_size_ = width * height * 3 / 2; + expected_size_ = media::VideoFrame::AllocationSize( + media::VideoFrame::I420, gfx::Size(width, height)); } void NewVideoFrame(const void* buffer) { if (file_.get() != NULL) { @@ -146,7 +148,8 @@ class MockVideoCaptureHost : public VideoCaptureHost { // These handler methods do minimal things and delegate to the mock methods. void OnNewBufferCreatedDispatch(int device_id, base::SharedMemoryHandle handle, - int length, int buffer_id) { + uint32 length, + int buffer_id) { OnNewBufferCreated(device_id, handle, length, buffer_id); base::SharedMemory* dib = new base::SharedMemory(handle, false); dib->Map(length); @@ -249,14 +252,31 @@ class VideoCaptureHostTest : public testing::Test { media::VideoCaptureParams params; params.width = 352; params.height = 288; - params.frame_per_second = 30; - params.session_id = kTestFakeDeviceId; + params.frame_rate = 30; + params.session_id = kTestFakeSessionId; host_->OnStartCapture(kDeviceId, params); run_loop.Run(); } + void StartStopCapture() { + // Quickly start and then stop capture, without giving much chance for + // asynchronous start operations to complete. + InSequence s; + base::RunLoop run_loop; + EXPECT_CALL(*host_.get(), + OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)); + media::VideoCaptureParams params; + params.width = 352; + params.height = 288; + params.frame_rate = 30; + params.session_id = kTestFakeSessionId; + host_->OnStartCapture(kDeviceId, params); + host_->OnStopCapture(kDeviceId); + run_loop.RunUntilIdle(); + } + #ifdef DUMP_VIDEO - void CaptureAndDumpVideo(int width, int heigt, int frame_rate) { + void CaptureAndDumpVideo(int width, int height, int frame_rate) { InSequence s; // 1. First - get info about the new resolution EXPECT_CALL(*host_, OnDeviceInfo(kDeviceId)); @@ -272,9 +292,9 @@ class VideoCaptureHostTest : public testing::Test { media::VideoCaptureParams params; params.width = width; - params.height = heigt; - params.frame_per_second = frame_rate; - params.session_id = kTestFakeDeviceId; + params.height = height; + params.frame_rate = frame_rate; + params.session_id = kTestFakeSessionId; host_->SetDumpVideo(true); host_->OnStartCapture(kDeviceId, params); run_loop.Run(); @@ -336,6 +356,12 @@ TEST_F(VideoCaptureHostTest, StartCapture) { StartCapture(); } +// Disabled because of a sometimes race between completion of implicit device +// enumeration and the capture stop. http://crbug.com/289684 +TEST_F(VideoCaptureHostTest, DISABLED_StopWhileStartOpening) { + StartStopCapture(); +} + TEST_F(VideoCaptureHostTest, StartCapturePlayStop) { StartCapture(); NotifyPacketReady(); diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.cc b/chromium/content/browser/renderer_host/media/video_capture_manager.cc index 79da41260a3..f61aa83d093 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_manager.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" +#include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/threading/sequenced_worker_pool.h" #include "content/browser/renderer_host/media/video_capture_controller.h" @@ -33,20 +34,15 @@ namespace content { // explicitly calling open device. enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 }; -struct VideoCaptureManager::Controller { - Controller( - VideoCaptureController* vc_controller, - VideoCaptureControllerEventHandler* handler) - : controller(vc_controller), - ready_to_delete(false) { - handlers.push_front(handler); - } - ~Controller() {} +VideoCaptureManager::DeviceEntry::DeviceEntry( + MediaStreamType stream_type, + const std::string& id, + scoped_ptr<VideoCaptureController> controller) + : stream_type(stream_type), + id(id), + video_capture_controller(controller.Pass()) {} - scoped_refptr<VideoCaptureController> controller; - bool ready_to_delete; - Handlers handlers; -}; +VideoCaptureManager::DeviceEntry::~DeviceEntry() {} VideoCaptureManager::VideoCaptureManager() : listener_(NULL), @@ -56,7 +52,6 @@ VideoCaptureManager::VideoCaptureManager() VideoCaptureManager::~VideoCaptureManager() { DCHECK(devices_.empty()); - DCHECK(controllers_.empty()); } void VideoCaptureManager::Register(MediaStreamProviderListener* listener, @@ -75,109 +70,87 @@ void VideoCaptureManager::Unregister() { void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type; DCHECK(listener_); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnEnumerateDevices, this, stream_type)); + base::PostTaskAndReplyWithResult( + device_loop_, FROM_HERE, + base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, this, + stream_type), + base::Bind(&VideoCaptureManager::OnDevicesEnumerated, this, stream_type)); } -int VideoCaptureManager::Open(const StreamDeviceInfo& device) { +int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(listener_); - // Generate a new id for this device. - int video_capture_session_id = new_capture_session_id_++; + // Generate a new id for the session being opened. + const int capture_session_id = new_capture_session_id_++; - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnOpen, this, video_capture_session_id, - device)); + DCHECK(sessions_.find(capture_session_id) == sessions_.end()); + DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id; - return video_capture_session_id; + // We just save the stream info for processing later. + sessions_[capture_session_id] = device_info.device; + + // Notify our listener asynchronously; this ensures that we return + // |capture_session_id| to the caller of this function before using that same + // id in a listener event. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&VideoCaptureManager::OnOpened, this, + device_info.device.type, capture_session_id)); + return capture_session_id; } void VideoCaptureManager::Close(int capture_session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(listener_); DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id; - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnClose, this, capture_session_id)); -} -void VideoCaptureManager::Start( - const media::VideoCaptureParams& capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnStart, this, capture_params, - video_capture_receiver)); -} + std::map<int, MediaStreamDevice>::iterator session_it = + sessions_.find(capture_session_id); + if (session_it == sessions_.end()) { + NOTREACHED(); + return; + } -void VideoCaptureManager::Stop( - const media::VideoCaptureSessionId& capture_session_id, - base::Closure stopped_cb) { - DVLOG(1) << "VideoCaptureManager::Stop, id " << capture_session_id; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnStop, this, capture_session_id, - stopped_cb)); + DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice( + session_it->second); + if (existing_device) { + // Remove any client that is still using the session. This is safe to call + // even if there are no clients using the session. + existing_device->video_capture_controller->StopSession(capture_session_id); + + // StopSession() may have removed the last client, so we might need to + // close the device. + DestroyDeviceEntryIfNoClients(existing_device); + } + + // Notify listeners asynchronously, and forget the session. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type, + capture_session_id)); + sessions_.erase(session_it); } void VideoCaptureManager::UseFakeDevice() { use_fake_device_ = true; } -void VideoCaptureManager::OnEnumerateDevices(MediaStreamType stream_type) { - SCOPED_UMA_HISTOGRAM_TIMER( - "Media.VideoCaptureManager.OnEnumerateDevicesTime"); - DCHECK(IsOnDeviceThread()); - - media::VideoCaptureDevice::Names device_names; - GetAvailableDevices(stream_type, &device_names); - - scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray()); - for (media::VideoCaptureDevice::Names::iterator it = - device_names.begin(); it != device_names.end(); ++it) { - bool opened = DeviceOpened(*it); - devices->push_back(StreamDeviceInfo( - stream_type, it->GetNameAndModel(), it->id(), opened)); - } - - PostOnDevicesEnumerated(stream_type, devices.Pass()); -} - -void VideoCaptureManager::OnOpen(int capture_session_id, - const StreamDeviceInfo& device) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnOpenTime"); +void VideoCaptureManager::DoStartDeviceOnDeviceThread( + DeviceEntry* entry, + const media::VideoCaptureCapability& capture_params, + scoped_ptr<media::VideoCaptureDevice::EventHandler> device_client) { + SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); DCHECK(IsOnDeviceThread()); - DCHECK(devices_.find(capture_session_id) == devices_.end()); - DVLOG(1) << "VideoCaptureManager::OnOpen, id " << capture_session_id; - - // Check if another session has already opened this device. If so, just - // use that opened device. - media::VideoCaptureDevice* opened_video_capture_device = - GetOpenedDevice(device); - if (opened_video_capture_device) { - DeviceEntry& new_entry = devices_[capture_session_id]; - new_entry.stream_type = device.device.type; - new_entry.capture_device = opened_video_capture_device; - PostOnOpened(device.device.type, capture_session_id); - return; - } scoped_ptr<media::VideoCaptureDevice> video_capture_device; - - // Open the device. - switch (device.device.type) { + switch (entry->stream_type) { case MEDIA_DEVICE_VIDEO_CAPTURE: { // We look up the device id from the renderer in our local enumeration // since the renderer does not have all the information that might be // held in the browser-side VideoCaptureDevice::Name structure. media::VideoCaptureDevice::Name* found = - video_capture_devices_.FindById(device.device.id); + video_capture_devices_.FindById(entry->id); if (found) { video_capture_device.reset(use_fake_device_ ? media::FakeVideoCaptureDevice::Create(*found) : @@ -187,12 +160,12 @@ void VideoCaptureManager::OnOpen(int capture_session_id, } case MEDIA_TAB_VIDEO_CAPTURE: { video_capture_device.reset( - WebContentsVideoCaptureDevice::Create(device.device.id)); + WebContentsVideoCaptureDevice::Create(entry->id)); break; } case MEDIA_DESKTOP_VIDEO_CAPTURE: { #if defined(ENABLE_SCREEN_CAPTURE) - DesktopMediaID id = DesktopMediaID::Parse(device.device.id); + DesktopMediaID id = DesktopMediaID::Parse(entry->id); if (id.type != DesktopMediaID::TYPE_NONE) { video_capture_device = DesktopCaptureDevice::Create(id); } @@ -206,128 +179,123 @@ void VideoCaptureManager::OnOpen(int capture_session_id, } if (!video_capture_device) { - PostOnError(capture_session_id, kDeviceNotAvailable); + device_client->OnError(); return; } - DeviceEntry& new_entry = devices_[capture_session_id]; - new_entry.stream_type = device.device.type; - new_entry.capture_device = video_capture_device.release(); - PostOnOpened(device.device.type, capture_session_id); + video_capture_device->AllocateAndStart(capture_params, device_client.Pass()); + entry->video_capture_device = video_capture_device.Pass(); } -void VideoCaptureManager::OnClose(int capture_session_id) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnCloseTime"); - DCHECK(IsOnDeviceThread()); - DVLOG(1) << "VideoCaptureManager::OnClose, id " << capture_session_id; - - VideoCaptureDevices::iterator device_it = devices_.find(capture_session_id); - if (device_it == devices_.end()) { +void VideoCaptureManager::StartCaptureForClient( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, (" + << capture_params.width + << ", " << capture_params.height + << ", " << capture_params.frame_rate + << ", #" << capture_params.session_id + << ")"; + + if (capture_params.session_id == kStartOpenSessionId) { + // Solution for not using MediaStreamManager. Enumerate the devices and + // open the first one, and then start it. + base::PostTaskAndReplyWithResult(device_loop_, FROM_HERE, + base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, + this, MEDIA_DEVICE_VIDEO_CAPTURE), + base::Bind(&VideoCaptureManager::OpenAndStartDefaultSession, this, + capture_params, client_render_process, client_id, + client_handler, done_cb)); return; + } else { + DoStartCaptureForClient(capture_params, client_render_process, client_id, + client_handler, done_cb); } - const DeviceEntry removed_entry = device_it->second; - devices_.erase(device_it); - - Controllers::iterator cit = controllers_.find(removed_entry.capture_device); - if (cit != controllers_.end()) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::StopSession, - cit->second->controller, capture_session_id)); - } +} - if (!DeviceInUse(removed_entry.capture_device)) { - // No other users of this device, deallocate (if not done already) and - // delete the device. No need to take care of the controller, that is done - // by |OnStop|. - removed_entry.capture_device->DeAllocate(); - Controllers::iterator cit = controllers_.find(removed_entry.capture_device); - if (cit != controllers_.end()) { - delete cit->second; - controllers_.erase(cit); - } - delete removed_entry.capture_device; +void VideoCaptureManager::DoStartCaptureForClient( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + DeviceEntry* entry = GetOrCreateDeviceEntry(capture_params.session_id); + if (!entry) { + done_cb.Run(base::WeakPtr<VideoCaptureController>()); + return; } - PostOnClosed(removed_entry.stream_type, capture_session_id); -} + DCHECK(entry->video_capture_controller); -void VideoCaptureManager::OnStart( - const media::VideoCaptureParams capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStartTime"); - DCHECK(IsOnDeviceThread()); - DCHECK(video_capture_receiver != NULL); - DVLOG(1) << "VideoCaptureManager::OnStart, (" << capture_params.width - << ", " << capture_params.height - << ", " << capture_params.frame_per_second - << ", " << capture_params.session_id - << ")"; - - media::VideoCaptureDevice* video_capture_device = - GetDeviceInternal(capture_params.session_id); - if (!video_capture_device) { - // Invalid session id. - video_capture_receiver->OnError(); - return; + // First client starts the device. + if (entry->video_capture_controller->GetClientCount() == 0) { + DVLOG(1) << "VideoCaptureManager starting device (type = " + << entry->stream_type << ", id = " << entry->id << ")"; + + media::VideoCaptureCapability params_as_capability; + params_as_capability.width = capture_params.width; + params_as_capability.height = capture_params.height; + params_as_capability.frame_rate = capture_params.frame_rate; + params_as_capability.session_id = capture_params.session_id; + params_as_capability.frame_size_type = capture_params.frame_size_type; + + device_loop_->PostTask(FROM_HERE, base::Bind( + &VideoCaptureManager::DoStartDeviceOnDeviceThread, this, + entry, params_as_capability, + base::Passed(entry->video_capture_controller->NewDeviceClient()))); } - // TODO(mcasas): Variable resolution video capture devices, are not yet - // fully supported, see crbug.com/261410, second part, and crbug.com/266082 . - if (capture_params.frame_size_type != - media::ConstantResolutionVideoCaptureDevice) { - LOG(DFATAL) << "Only constant Video Capture resolution device supported."; - video_capture_receiver->OnError(); + // Run the callback first, as AddClient() may trigger OnFrameInfo(). + done_cb.Run(entry->video_capture_controller->GetWeakPtr()); + entry->video_capture_controller->AddClient(client_id, + client_handler, + client_render_process, + capture_params); +} + +void VideoCaptureManager::StopCaptureForClient( + VideoCaptureController* controller, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(controller); + DCHECK(client_handler); + + DeviceEntry* entry = GetDeviceEntryForController(controller); + if (!entry) { + NOTREACHED(); return; } - Controllers::iterator cit = controllers_.find(video_capture_device); - if (cit != controllers_.end()) { - cit->second->ready_to_delete = false; - } - // Possible errors are signaled to video_capture_receiver by - // video_capture_device. video_capture_receiver to perform actions. - media::VideoCaptureCapability params_as_capability_copy; - params_as_capability_copy.width = capture_params.width; - params_as_capability_copy.height = capture_params.height; - params_as_capability_copy.frame_rate = capture_params.frame_per_second; - params_as_capability_copy.session_id = capture_params.session_id; - params_as_capability_copy.frame_size_type = capture_params.frame_size_type; - video_capture_device->Allocate(params_as_capability_copy, - video_capture_receiver); - video_capture_device->Start(); -} + // Detach client from controller. + int session_id = controller->RemoveClient(client_id, client_handler); + DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = " + << session_id; -void VideoCaptureManager::OnStop( - const media::VideoCaptureSessionId capture_session_id, - base::Closure stopped_cb) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStopTime"); - DCHECK(IsOnDeviceThread()); - DVLOG(1) << "VideoCaptureManager::OnStop, id " << capture_session_id; - - VideoCaptureDevices::iterator it = devices_.find(capture_session_id); - if (it != devices_.end()) { - media::VideoCaptureDevice* video_capture_device = it->second.capture_device; - // Possible errors are signaled to video_capture_receiver by - // video_capture_device. video_capture_receiver to perform actions. - video_capture_device->Stop(); - video_capture_device->DeAllocate(); - Controllers::iterator cit = controllers_.find(video_capture_device); - if (cit != controllers_.end()) { - cit->second->ready_to_delete = true; - if (cit->second->handlers.empty()) { - delete cit->second; - controllers_.erase(cit); - } - } - } + // If controller has no more clients, delete controller and device. + DestroyDeviceEntryIfNoClients(entry); - if (!stopped_cb.is_null()) - stopped_cb.Run(); + // Close the session if it was auto-opened by StartCaptureForClient(). + if (session_id == kStartOpenSessionId) { + sessions_.erase(session_id); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&VideoCaptureManager::OnClosed, this, + MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); + } +} - if (capture_session_id == kStartOpenSessionId) { - // This device was opened from Start(), not Open(). Close it! - OnClose(capture_session_id); +void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { + SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); + DCHECK(IsOnDeviceThread()); + if (entry->video_capture_device) { + entry->video_capture_device->StopAndDeAllocate(); } + entry->video_capture_device.reset(); } void VideoCaptureManager::OnOpened(MediaStreamType stream_type, @@ -352,242 +320,173 @@ void VideoCaptureManager::OnClosed(MediaStreamType stream_type, void VideoCaptureManager::OnDevicesEnumerated( MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices) { + const media::VideoCaptureDevice::Names& device_names) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!listener_) { - // Listener has been removed. - return; - } - listener_->DevicesEnumerated(stream_type, *devices); -} -void VideoCaptureManager::OnError(MediaStreamType stream_type, - int capture_session_id, - MediaStreamProviderError error) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!listener_) { // Listener has been removed. return; } - listener_->Error(stream_type, capture_session_id, error); -} -void VideoCaptureManager::PostOnOpened( - MediaStreamType stream_type, int capture_session_id) { - DCHECK(IsOnDeviceThread()); - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureManager::OnOpened, this, - stream_type, capture_session_id)); -} - -void VideoCaptureManager::PostOnClosed( - MediaStreamType stream_type, int capture_session_id) { - DCHECK(IsOnDeviceThread()); - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureManager::OnClosed, this, - stream_type, capture_session_id)); -} - -void VideoCaptureManager::PostOnDevicesEnumerated( - MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices) { - DCHECK(IsOnDeviceThread()); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureManager::OnDevicesEnumerated, - this, stream_type, base::Passed(&devices))); -} + // Transform from VCD::Name to StreamDeviceInfo. + StreamDeviceInfoArray devices; + for (media::VideoCaptureDevice::Names::const_iterator it = + device_names.begin(); it != device_names.end(); ++it) { + devices.push_back(StreamDeviceInfo( + stream_type, it->GetNameAndModel(), it->id(), false)); + } -void VideoCaptureManager::PostOnError(int capture_session_id, - MediaStreamProviderError error) { - DCHECK(IsOnDeviceThread()); - MediaStreamType stream_type = MEDIA_DEVICE_VIDEO_CAPTURE; - VideoCaptureDevices::const_iterator it = devices_.find(capture_session_id); - if (it != devices_.end()) - stream_type = it->second.stream_type; - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureManager::OnError, this, - stream_type, capture_session_id, error)); + listener_->DevicesEnumerated(stream_type, devices); } bool VideoCaptureManager::IsOnDeviceThread() const { return device_loop_->BelongsToCurrentThread(); } -void VideoCaptureManager::GetAvailableDevices( - MediaStreamType stream_type, - media::VideoCaptureDevice::Names* device_names) { +media::VideoCaptureDevice::Names +VideoCaptureManager::GetAvailableDevicesOnDeviceThread( + MediaStreamType stream_type) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Media.VideoCaptureManager.GetAvailableDevicesTime"); DCHECK(IsOnDeviceThread()); + media::VideoCaptureDevice::Names result; switch (stream_type) { case MEDIA_DEVICE_VIDEO_CAPTURE: // Cache the latest enumeration of video capture devices. // We'll refer to this list again in OnOpen to avoid having to // enumerate the devices again. - video_capture_devices_.clear(); if (!use_fake_device_) { - media::VideoCaptureDevice::GetDeviceNames(&video_capture_devices_); + media::VideoCaptureDevice::GetDeviceNames(&result); } else { - media::FakeVideoCaptureDevice::GetDeviceNames(&video_capture_devices_); + media::FakeVideoCaptureDevice::GetDeviceNames(&result); } - *device_names = video_capture_devices_; + + // TODO(nick): The correctness of device start depends on this cache being + // maintained, but it seems a little odd to keep a cache here. Can we + // eliminate it? + video_capture_devices_ = result; break; case MEDIA_DESKTOP_VIDEO_CAPTURE: - device_names->clear(); + // Do nothing. break; default: NOTREACHED(); break; } + return result; } -bool VideoCaptureManager::DeviceOpened( - const media::VideoCaptureDevice::Name& device_name) { - DCHECK(IsOnDeviceThread()); +VideoCaptureManager::DeviceEntry* +VideoCaptureManager::GetDeviceEntryForMediaStreamDevice( + const MediaStreamDevice& device_info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - for (VideoCaptureDevices::iterator it = devices_.begin(); + for (DeviceEntries::iterator it = devices_.begin(); it != devices_.end(); ++it) { - if (device_name.id() == it->second.capture_device->device_name().id()) { - // We've found the device! - return true; - } - } - return false; -} - -media::VideoCaptureDevice* VideoCaptureManager::GetOpenedDevice( - const StreamDeviceInfo& device_info) { - DCHECK(IsOnDeviceThread()); - - for (VideoCaptureDevices::iterator it = devices_.begin(); - it != devices_.end(); it++) { - if (device_info.device.id == - it->second.capture_device->device_name().id()) { - return it->second.capture_device; + DeviceEntry* device = *it; + if (device_info.type == device->stream_type && + device_info.id == device->id) { + return device; } } return NULL; } -bool VideoCaptureManager::DeviceInUse( - const media::VideoCaptureDevice* video_capture_device) { - DCHECK(IsOnDeviceThread()); - - for (VideoCaptureDevices::iterator it = devices_.begin(); +VideoCaptureManager::DeviceEntry* +VideoCaptureManager::GetDeviceEntryForController( + const VideoCaptureController* controller) { + // Look up |controller| in |devices_|. + for (DeviceEntries::iterator it = devices_.begin(); it != devices_.end(); ++it) { - if (video_capture_device == it->second.capture_device) { - // We've found the device! - return true; + if ((*it)->video_capture_controller.get() == controller) { + return *it; } } - return false; + return NULL; } -void VideoCaptureManager::AddController( +void VideoCaptureManager::OpenAndStartDefaultSession( const media::VideoCaptureParams& capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb) { - DCHECK(handler); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread, - this, capture_params, handler, added_cb)); -} - -void VideoCaptureManager::DoAddControllerOnDeviceThread( - const media::VideoCaptureParams capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb) { - DCHECK(IsOnDeviceThread()); + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb, + const media::VideoCaptureDevice::Names& device_names) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - media::VideoCaptureDevice* video_capture_device = - GetDeviceInternal(capture_params.session_id); - scoped_refptr<VideoCaptureController> controller; - if (video_capture_device) { - Controllers::iterator cit = controllers_.find(video_capture_device); - if (cit == controllers_.end()) { - controller = new VideoCaptureController(this); - controllers_[video_capture_device] = - new Controller(controller.get(), handler); - } else { - controllers_[video_capture_device]->handlers.push_front(handler); - controller = controllers_[video_capture_device]->controller; - } + // |device_names| is a value returned by GetAvailableDevicesOnDeviceThread(). + // We'll mimic an Open() operation on the first element in that list. + DCHECK(capture_params.session_id == kStartOpenSessionId); + if (device_names.empty() || + sessions_.count(capture_params.session_id) != 0) { + done_cb.Run(base::WeakPtr<VideoCaptureController>()); + return; } - added_cb.Run(controller.get()); -} -void VideoCaptureManager::RemoveController( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler) { - DCHECK(handler); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread, this, - make_scoped_refptr(controller), handler)); -} + // Open the device by creating a |sessions_| entry. + sessions_[capture_params.session_id] = + MediaStreamDevice(MEDIA_DEVICE_VIDEO_CAPTURE, + device_names.front().id(), + device_names.front().GetNameAndModel()); -void VideoCaptureManager::DoRemoveControllerOnDeviceThread( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler) { - DCHECK(IsOnDeviceThread()); + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&VideoCaptureManager::OnOpened, this, + MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); - for (Controllers::iterator cit = controllers_.begin(); - cit != controllers_.end(); ++cit) { - if (controller == cit->second->controller.get()) { - Handlers& handlers = cit->second->handlers; - for (Handlers::iterator hit = handlers.begin(); - hit != handlers.end(); ++hit) { - if ((*hit) == handler) { - handlers.erase(hit); - break; - } - } - if (handlers.empty() && cit->second->ready_to_delete) { - delete cit->second; - controllers_.erase(cit); - } - return; - } - } + DoStartCaptureForClient(capture_params, client_render_process, client_id, + client_handler, done_cb); } -media::VideoCaptureDevice* VideoCaptureManager::GetDeviceInternal( - int capture_session_id) { - DCHECK(IsOnDeviceThread()); - VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); - if (dit != devices_.end()) { - return dit->second.capture_device; +void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Removal of the last client stops the device. + if (entry->video_capture_controller->GetClientCount() == 0) { + DVLOG(1) << "VideoCaptureManager stopping device (type = " + << entry->stream_type << ", id = " << entry->id << ")"; + + // The DeviceEntry is removed from |devices_| immediately. The controller is + // deleted immediately, and the device is freed asynchronously. After this + // point, subsequent requests to open this same device ID will create a new + // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. + devices_.erase(entry); + entry->video_capture_controller.reset(); + device_loop_->PostTask( + FROM_HERE, + base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, + base::Owned(entry))); } +} - // Solution for not using MediaStreamManager. - // This session id won't be returned by Open(). - if (capture_session_id == kStartOpenSessionId) { - media::VideoCaptureDevice::Names device_names; - GetAvailableDevices(MEDIA_DEVICE_VIDEO_CAPTURE, &device_names); - if (device_names.empty()) { - // No devices available. - return NULL; - } - StreamDeviceInfo device(MEDIA_DEVICE_VIDEO_CAPTURE, - device_names.front().GetNameAndModel(), - device_names.front().id(), - false); +VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( + int capture_session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // Call OnOpen to open using the first device in the list. - OnOpen(capture_session_id, device); + std::map<int, MediaStreamDevice>::iterator session_it = + sessions_.find(capture_session_id); + if (session_it == sessions_.end()) { + return NULL; + } + const MediaStreamDevice& device_info = session_it->second; - VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); - if (dit != devices_.end()) { - return dit->second.capture_device; - } + // Check if another session has already opened this device. If so, just + // use that opened device. + DeviceEntry* const existing_device = + GetDeviceEntryForMediaStreamDevice(device_info); + if (existing_device) { + DCHECK_EQ(device_info.type, existing_device->stream_type); + return existing_device; } - return NULL; + + scoped_ptr<VideoCaptureController> video_capture_controller( + new VideoCaptureController()); + DeviceEntry* new_device = new DeviceEntry(device_info.type, + device_info.id, + video_capture_controller.Pass()); + devices_.insert(new_device); + return new_device; } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.h b/chromium/content/browser/renderer_host/media/video_capture_manager.h index 34d6e626413..fc6dd92fecd 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager.h +++ b/chromium/content/browser/renderer_host/media/video_capture_manager.h @@ -4,31 +4,39 @@ // VideoCaptureManager is used to open/close, start/stop, enumerate available // video capture devices, and manage VideoCaptureController's. -// All functions are expected to be called from Browser::IO thread. +// All functions are expected to be called from Browser::IO thread. Some helper +// functions (*OnDeviceThread) will dispatch operations to the device thread. // VideoCaptureManager will open OS dependent instances of VideoCaptureDevice. // A device can only be opened once. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_ -#include <list> #include <map> +#include <set> +#include <string> #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/process/process_handle.h" #include "content/browser/renderer_host/media/media_stream_provider.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/common/content_export.h" #include "content/common/media/media_stream_options.h" #include "media/video/capture/video_capture_device.h" #include "media/video/capture/video_capture_types.h" namespace content { -class MockVideoCaptureManager; class VideoCaptureController; class VideoCaptureControllerEventHandler; // VideoCaptureManager opens/closes and start/stops video capture devices. class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { public: + // Callback used to signal the completion of a controller lookup. + typedef base::Callback< + void(const base::WeakPtr<VideoCaptureController>&)> DoneCB; + // Calling |Start| of this id will open the first device, even though open has // not been called. This is used to be able to use video capture devices // before MediaStream is implemented in Chrome and WebKit. @@ -48,118 +56,147 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { virtual void Close(int capture_session_id) OVERRIDE; - // Functions used to start and stop media flow. - // Start allocates the device and no other application can use the device - // before Stop is called. Captured video frames will be delivered to - // video_capture_receiver. - virtual void Start(const media::VideoCaptureParams& capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver); - - // Stops capture device referenced by |capture_session_id|. No more frames - // will be delivered to the frame receiver, and |stopped_cb| will be called. - // |stopped_cb| can be NULL. - virtual void Stop(const media::VideoCaptureSessionId& capture_session_id, - base::Closure stopped_cb); - // Used by unit test to make sure a fake device is used instead of a real // video capture device. Due to timing requirements, the function must be // called before EnumerateDevices and Open. void UseFakeDevice(); - // Called by VideoCaptureHost to get a controller for |capture_params|. - // The controller is returned via calling |added_cb|. - void AddController( - const media::VideoCaptureParams& capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb); - // Called by VideoCaptureHost to remove the |controller|. - void RemoveController( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler); + // Called by VideoCaptureHost to locate a capture device for |capture_params|, + // adding the Host as a client of the device's controller if successful. The + // value of |capture_params.session_id| controls which device is selected; + // this value should be a session id previously returned by Open(). + // + // If the device is not already started (i.e., no other client is currently + // capturing from this device), this call will cause a VideoCaptureController + // and VideoCaptureDevice to be created, possibly asynchronously. + // + // On success, the controller is returned via calling |done_cb|, indicating + // that the client was successfully added. A NULL controller is passed to + // the callback on failure. + void StartCaptureForClient(const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb); + + // Called by VideoCaptureHost to remove |client_handler|. If this is the last + // client of the device, the |controller| and its VideoCaptureDevice may be + // destroyed. The client must not access |controller| after calling this + // function. + void StopCaptureForClient(VideoCaptureController* controller, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler); private: - friend class MockVideoCaptureManager; - virtual ~VideoCaptureManager(); + struct DeviceEntry; - typedef std::list<VideoCaptureControllerEventHandler*> Handlers; - struct Controller; - - // Called by the public functions, executed on device thread. - void OnEnumerateDevices(MediaStreamType stream_type); - void OnOpen(int capture_session_id, const StreamDeviceInfo& device); - void OnClose(int capture_session_id); - void OnStart(const media::VideoCaptureParams capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver); - void OnStop(const media::VideoCaptureSessionId capture_session_id, - base::Closure stopped_cb); - void DoAddControllerOnDeviceThread( - const media::VideoCaptureParams capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb); - void DoRemoveControllerOnDeviceThread( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler); - - // Executed on Browser::IO thread to call Listener. + // Helper for the kStartOpenSessionId case. + void OpenAndStartDefaultSession( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb, + const media::VideoCaptureDevice::Names& device_names); + + // Helper routine implementing StartCaptureForClient(). + void DoStartCaptureForClient( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb); + + // Check to see if |entry| has no clients left on its controller. If so, + // remove it from the list of devices, and delete it asynchronously. |entry| + // may be freed by this function. + void DestroyDeviceEntryIfNoClients(DeviceEntry* entry); + + // Helpers to report an event to our Listener. void OnOpened(MediaStreamType type, int capture_session_id); void OnClosed(MediaStreamType type, int capture_session_id); void OnDevicesEnumerated(MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices); - void OnError(MediaStreamType type, int capture_session_id, - MediaStreamProviderError error); - - // Executed on device thread to make sure Listener is called from - // Browser::IO thread. - void PostOnOpened(MediaStreamType type, int capture_session_id); - void PostOnClosed(MediaStreamType type, int capture_session_id); - void PostOnDevicesEnumerated(MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices); - void PostOnError(int capture_session_id, MediaStreamProviderError error); - - // Helpers - void GetAvailableDevices(MediaStreamType stream_type, - media::VideoCaptureDevice::Names* device_names); - bool DeviceOpened(const media::VideoCaptureDevice::Name& device_name); - bool DeviceInUse(const media::VideoCaptureDevice* video_capture_device); - media::VideoCaptureDevice* GetOpenedDevice( - const StreamDeviceInfo& device_info); + const media::VideoCaptureDevice::Names& names); + + // Find a DeviceEntry by its device ID and type, if it is already opened. + DeviceEntry* GetDeviceEntryForMediaStreamDevice( + const MediaStreamDevice& device_info); + + // Find a DeviceEntry entry for the indicated session, creating a fresh one + // if necessary. Returns NULL if the session id is invalid. + DeviceEntry* GetOrCreateDeviceEntry(int capture_session_id); + + // Find the DeviceEntry that owns a particular controller pointer. + DeviceEntry* GetDeviceEntryForController( + const VideoCaptureController* controller); + bool IsOnDeviceThread() const; - media::VideoCaptureDevice* GetDeviceInternal(int capture_session_id); - // The message loop of media stream device thread that this object runs on. + // Queries and returns the available device IDs. + media::VideoCaptureDevice::Names GetAvailableDevicesOnDeviceThread( + MediaStreamType stream_type); + + // Create and Start a new VideoCaptureDevice, storing the result in + // |entry->video_capture_device|. Ownership of |handler| passes to + // the device. + void DoStartDeviceOnDeviceThread( + DeviceEntry* entry, + const media::VideoCaptureCapability& capture_params, + scoped_ptr<media::VideoCaptureDevice::EventHandler> handler); + + // Stop and destroy the VideoCaptureDevice held in + // |entry->video_capture_device|. + void DoStopDeviceOnDeviceThread(DeviceEntry* entry); + + // The message loop of media stream device thread, where VCD's live. scoped_refptr<base::MessageLoopProxy> device_loop_; // Only accessed on Browser::IO thread. MediaStreamProviderListener* listener_; int new_capture_session_id_; - // Only accessed from device thread. - // VideoCaptureManager owns all VideoCaptureDevices and is responsible for - // deleting the instances when they are not used any longer. + // An entry is kept in this map for every session that has been created via + // the Open() entry point. The keys are session_id's. This map is used to + // determine which device to use when StartCaptureForClient() occurs. Used + // only on the IO thread. + std::map<int, MediaStreamDevice> sessions_; + + // An entry, kept in a map, that owns a VideoCaptureDevice and its associated + // VideoCaptureController. VideoCaptureManager owns all VideoCaptureDevices + // and VideoCaptureControllers and is responsible for deleting the instances + // when they are not used any longer. + // + // The set of currently started VideoCaptureDevice and VideoCaptureController + // objects is only accessed from IO thread, though the DeviceEntry instances + // themselves may visit to the device thread for device creation and + // destruction. struct DeviceEntry { - MediaStreamType stream_type; - media::VideoCaptureDevice* capture_device; // Maybe shared across sessions. + DeviceEntry(MediaStreamType stream_type, + const std::string& id, + scoped_ptr<VideoCaptureController> controller); + ~DeviceEntry(); + + const MediaStreamType stream_type; + const std::string id; + + // The controller. Only used from the IO thread. + scoped_ptr<VideoCaptureController> video_capture_controller; + + // The capture device. Only used from the device thread. + scoped_ptr<media::VideoCaptureDevice> video_capture_device; }; - typedef std::map<int, DeviceEntry> VideoCaptureDevices; - VideoCaptureDevices devices_; // Maps capture_session_id to DeviceEntry. + typedef std::set<DeviceEntry*> DeviceEntries; + DeviceEntries devices_; - // Set to true if using fake video capture devices for testing, - // false by default. This is only used for the MEDIA_DEVICE_VIDEO_CAPTURE - // device type. + // Set to true if using fake video capture devices for testing, false by + // default. This is only used for the MEDIA_DEVICE_VIDEO_CAPTURE device type. bool use_fake_device_; - // Only accessed from device thread. - // VideoCaptureManager owns all VideoCaptureController's and is responsible - // for deleting the instances when they are not used any longer. - // VideoCaptureDevice is one-to-one mapped to VideoCaptureController. - typedef std::map<media::VideoCaptureDevice*, Controller*> Controllers; - Controllers controllers_; - - // We cache the enumerated video capture devices in GetAvailableDevices - // (e.g. called by OnEnumerateDevices) and then look up the requested ID when - // a device is opened (see OnOpen). - // Used only on the device thread. + // We cache the enumerated video capture devices in + // GetAvailableDevicesOnDeviceThread() and then later look up the requested ID + // when a device is created in DoStartDeviceOnDeviceThread(). Used only on the + // device thread. media::VideoCaptureDevice::Names video_capture_devices_; DISALLOW_COPY_AND_ASSIGN(VideoCaptureManager); diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc index 83c064655ed..34172d78cb4 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc @@ -10,8 +10,10 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/renderer_host/media/media_stream_provider.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/common/media/media_stream_options.h" #include "media/video/capture/video_capture_device.h" @@ -40,30 +42,33 @@ class MockMediaStreamProviderListener : public MediaStreamProviderListener { MediaStreamProviderError)); }; // class MockMediaStreamProviderListener -// Needed as an input argument to Start(). -class MockFrameObserver : public media::VideoCaptureDevice::EventHandler { +// Needed as an input argument to StartCaptureForClient(). +class MockFrameObserver : public VideoCaptureControllerEventHandler { public: - virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE { - return NULL; - } - virtual void OnError() OVERRIDE {} + MOCK_METHOD1(OnError, void(const VideoCaptureControllerID& id)); + + virtual void OnBufferCreated(const VideoCaptureControllerID& id, + base::SharedMemoryHandle handle, + int length, int buffer_id) OVERRIDE {}; + virtual void OnBufferReady(const VideoCaptureControllerID& id, + int buffer_id, + base::Time timestamp) OVERRIDE {}; virtual void OnFrameInfo( - const media::VideoCaptureCapability& info) OVERRIDE {} - virtual void OnIncomingCapturedFrame(const uint8* data, - int length, - base::Time timestamp, - int rotation, - bool flip_vert, - bool flip_horiz) OVERRIDE {} - virtual void OnIncomingCapturedVideoFrame( - const scoped_refptr<media::VideoFrame>& frame, - base::Time timestamp) OVERRIDE {} + const VideoCaptureControllerID& id, + const media::VideoCaptureCapability& format) OVERRIDE {}; + virtual void OnFrameInfoChanged(const VideoCaptureControllerID& id, + int width, + int height, + int frame_rate) OVERRIDE {}; + virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE {}; + + void OnGotControllerCallback(VideoCaptureControllerID) {} }; // Test class class VideoCaptureManagerTest : public testing::Test { public: - VideoCaptureManagerTest() {} + VideoCaptureManagerTest() : next_client_id_(1) {} virtual ~VideoCaptureManagerTest() {} protected: @@ -80,6 +85,48 @@ class VideoCaptureManagerTest : public testing::Test { virtual void TearDown() OVERRIDE {} + void OnGotControllerCallback( + VideoCaptureControllerID id, + base::Closure quit_closure, + bool expect_success, + const base::WeakPtr<VideoCaptureController>& controller) { + if (expect_success) { + ASSERT_TRUE(controller); + ASSERT_TRUE(0 == controllers_.count(id)); + controllers_[id] = controller.get(); + } else { + ASSERT_TRUE(NULL == controller); + } + quit_closure.Run(); + } + + VideoCaptureControllerID StartClient(int session_id, bool expect_success) { + media::VideoCaptureParams params; + params.session_id = session_id; + params.width = 320; + params.height = 240; + params.frame_rate = 30; + + VideoCaptureControllerID client_id(next_client_id_++); + base::RunLoop run_loop; + vcm_->StartCaptureForClient( + params, base::kNullProcessHandle, client_id, frame_observer_.get(), + base::Bind(&VideoCaptureManagerTest::OnGotControllerCallback, + base::Unretained(this), client_id, run_loop.QuitClosure(), + expect_success)); + run_loop.Run(); + return client_id; + } + + void StopClient(VideoCaptureControllerID client_id) { + ASSERT_TRUE(1 == controllers_.count(client_id)); + vcm_->StopCaptureForClient(controllers_[client_id], client_id, + frame_observer_.get()); + controllers_.erase(client_id); + } + + int next_client_id_; + std::map<VideoCaptureControllerID, VideoCaptureController*> controllers_; scoped_refptr<VideoCaptureManager> vcm_; scoped_ptr<MockMediaStreamProviderListener> listener_; scoped_ptr<base::MessageLoop> message_loop_; @@ -108,15 +155,9 @@ TEST_F(VideoCaptureManagerTest, CreateAndClose) { message_loop_->RunUntilIdle(); int video_session_id = vcm_->Open(devices.front()); + VideoCaptureControllerID client_id = StartClient(video_session_id, true); - media::VideoCaptureParams capture_params; - capture_params.session_id = video_session_id; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - vcm_->Start(capture_params, frame_observer_.get()); - - vcm_->Stop(video_session_id, base::Closure()); + StopClient(client_id); vcm_->Close(video_session_id); // Wait to check callbacks before removing the listener. @@ -190,9 +231,9 @@ TEST_F(VideoCaptureManagerTest, OpenNotExisting) { InSequence s; EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _)) .Times(1).WillOnce(SaveArg<1>(&devices)); - EXPECT_CALL(*listener_, Error(MEDIA_DEVICE_VIDEO_CAPTURE, - _, kDeviceNotAvailable)) - .Times(1); + EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); + EXPECT_CALL(*frame_observer_, OnError(_)).Times(1); + EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE); @@ -204,11 +245,15 @@ TEST_F(VideoCaptureManagerTest, OpenNotExisting) { std::string device_id("id_doesnt_exist"); StreamDeviceInfo dummy_device(stream_type, device_name, device_id, false); - // This should fail with error code 'kDeviceNotAvailable'. - vcm_->Open(dummy_device); + // This should fail with an error to the controller. + int session_id = vcm_->Open(dummy_device); + VideoCaptureControllerID client_id = StartClient(session_id, true); + message_loop_->RunUntilIdle(); - // Wait to check callbacks before removing the listener. + StopClient(client_id); + vcm_->Close(session_id); message_loop_->RunUntilIdle(); + vcm_->Unregister(); } @@ -218,17 +263,21 @@ TEST_F(VideoCaptureManagerTest, StartUsingId) { EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); - media::VideoCaptureParams capture_params; - capture_params.session_id = VideoCaptureManager::kStartOpenSessionId; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - // Start shall trigger the Open callback. - vcm_->Start(capture_params, frame_observer_.get()); + VideoCaptureControllerID client_id = StartClient( + VideoCaptureManager::kStartOpenSessionId, true); // Stop shall trigger the Close callback - vcm_->Stop(VideoCaptureManager::kStartOpenSessionId, base::Closure()); + StopClient(client_id); + + // Wait to check callbacks before removing the listener. + message_loop_->RunUntilIdle(); + vcm_->Unregister(); +} + +// Start a device without calling Open, using a non-magic ID. +TEST_F(VideoCaptureManagerTest, StartInvalidSession) { + StartClient(22, false); // Wait to check callbacks before removing the listener. message_loop_->RunUntilIdle(); @@ -252,17 +301,12 @@ TEST_F(VideoCaptureManagerTest, CloseWithoutStop) { int video_session_id = vcm_->Open(devices.front()); - media::VideoCaptureParams capture_params; - capture_params.session_id = video_session_id; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - vcm_->Start(capture_params, frame_observer_.get()); + VideoCaptureControllerID client_id = StartClient(video_session_id, true); // Close will stop the running device, an assert will be triggered in // VideoCaptureManager destructor otherwise. vcm_->Close(video_session_id); - vcm_->Stop(video_session_id, base::Closure()); + StopClient(client_id); // Wait to check callbacks before removing the listener message_loop_->RunUntilIdle(); diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc index e27b703081e..91feb4d4873 100644 --- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc +++ b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc @@ -56,8 +56,8 @@ #include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" #include "base/callback_forward.h" +#include "base/callback_helpers.h" #include "base/debug/trace_event.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -693,7 +693,7 @@ void RenderVideoFrame(const SkBitmap& input, } // The result is now ready. - failure_handler.Release(); + ignore_result(failure_handler.Release()); done_cb.Run(true); } @@ -1076,7 +1076,7 @@ void WebContentsVideoCaptureDevice::Impl::Allocate( settings.frame_rate = frame_rate; // Note: the value of |settings.color| doesn't matter if we use only the // VideoFrame based methods on |consumer|. - settings.color = media::VideoCaptureCapability::kI420; + settings.color = media::PIXEL_FORMAT_I420; settings.expected_capture_delay = 0; settings.interlaced = false; @@ -1228,7 +1228,7 @@ WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { } // static -media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create( +media::VideoCaptureDevice1* WebContentsVideoCaptureDevice::Create( const std::string& device_id) { // Parse device_id into render_process_id and render_view_id. int render_process_id = -1; diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h index 94ac0680f6f..0dfe1d6e2f0 100644 --- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h +++ b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h @@ -27,7 +27,7 @@ class RenderWidgetHost; // underlying render view to be swapped out (e.g., due to navigation or // crashes/reloads), without any interruption in capturing. class CONTENT_EXPORT WebContentsVideoCaptureDevice - : public media::VideoCaptureDevice { + : public media::VideoCaptureDevice1 { public: // Construct from a |device_id| string of the form: // "virtual-media-stream://render_process_id:render_view_id", where @@ -37,7 +37,7 @@ class CONTENT_EXPORT WebContentsVideoCaptureDevice // WebContentsVideoCaptureDevice is itself deleted. // TODO(miu): Passing a destroy callback suggests needing to revisit the // design philosophy of an asynchronous DeAllocate(). http://crbug.com/158641 - static media::VideoCaptureDevice* Create(const std::string& device_id); + static media::VideoCaptureDevice1* Create(const std::string& device_id); virtual ~WebContentsVideoCaptureDevice(); diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc index d88d553fa73..3420b09eb7b 100644 --- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc +++ b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc @@ -310,8 +310,10 @@ class StubConsumer : public media::VideoCaptureDevice::EventHandler { StubConsumer() : error_encountered_(false), wait_color_yuv_(0xcafe1950) { - buffer_pool_ = - new VideoCaptureBufferPool(kTestWidth * kTestHeight * 3 / 2, 2); + buffer_pool_ = new VideoCaptureBufferPool( + media::VideoFrame::AllocationSize(media::VideoFrame::I420, + gfx::Size(kTestWidth, kTestHeight)), + 2); EXPECT_TRUE(buffer_pool_->Allocate()); } virtual ~StubConsumer() {} @@ -405,7 +407,7 @@ class StubConsumer : public media::VideoCaptureDevice::EventHandler { EXPECT_EQ(kTestWidth, info.width); EXPECT_EQ(kTestHeight, info.height); EXPECT_EQ(kTestFramesPerSecond, info.frame_rate); - EXPECT_EQ(media::VideoCaptureCapability::kI420, info.color); + EXPECT_EQ(media::PIXEL_FORMAT_I420, info.color); } private: @@ -491,7 +493,7 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { // Accessors. CaptureTestSourceController* source() { return &controller_; } - media::VideoCaptureDevice* device() { return device_.get(); } + media::VideoCaptureDevice1* device() { return device_.get(); } StubConsumer* consumer() { return &consumer_; } void SimulateDrawEvent() { @@ -530,7 +532,7 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { scoped_ptr<WebContents> web_contents_; // Finally, the WebContentsVideoCaptureDevice under test. - scoped_ptr<media::VideoCaptureDevice> device_; + scoped_ptr<media::VideoCaptureDevice1> device_; TestBrowserThreadBundle thread_bundle_; }; @@ -545,7 +547,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, InvalidInitialWebContentsError) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -562,7 +564,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -591,7 +593,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -617,7 +619,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, StopWithRendererWorkToDo) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -647,7 +649,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, DeviceRestart) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -689,7 +691,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, GoesThroughAllTheMotions) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -745,13 +747,13 @@ TEST_F(WebContentsVideoCaptureDeviceTest, RejectsInvalidAllocateParams) { 1280, 720, -2, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&media::VideoCaptureDevice::Allocate, + base::Bind(&media::VideoCaptureDevice1::Allocate, base::Unretained(device()), capture_format, consumer())); @@ -763,7 +765,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, BadFramesGoodFrames) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc index 0230b26eeaa..76c536baf7c 100644 --- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc +++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc @@ -36,13 +36,14 @@ bool WebRTCIdentityServiceHost::OnMessageReceived(const IPC::Message& message, } void WebRTCIdentityServiceHost::OnRequestIdentity( + int sequence_number, const GURL& origin, const std::string& identity_name, const std::string& common_name) { if (!cancel_callback_.is_null()) { DLOG(WARNING) << "Request rejected because the previous request has not finished."; - SendErrorMessage(net::ERR_INSUFFICIENT_RESOURCES); + SendErrorMessage(sequence_number, net::ERR_INSUFFICIENT_RESOURCES); return; } @@ -50,7 +51,7 @@ void WebRTCIdentityServiceHost::OnRequestIdentity( ChildProcessSecurityPolicyImpl::GetInstance(); if (!policy->CanAccessCookiesForOrigin(renderer_process_id_, origin)) { DLOG(WARNING) << "Request rejected because origin access is denied."; - SendErrorMessage(net::ERR_ACCESS_DENIED); + SendErrorMessage(sequence_number, net::ERR_ACCESS_DENIED); return; } @@ -59,9 +60,10 @@ void WebRTCIdentityServiceHost::OnRequestIdentity( identity_name, common_name, base::Bind(&WebRTCIdentityServiceHost::OnComplete, - base::Unretained(this))); + base::Unretained(this), + sequence_number)); if (cancel_callback_.is_null()) { - SendErrorMessage(net::ERR_UNEXPECTED); + SendErrorMessage(sequence_number, net::ERR_UNEXPECTED); } } @@ -69,19 +71,22 @@ void WebRTCIdentityServiceHost::OnCancelRequest() { base::ResetAndReturn(&cancel_callback_).Run(); } -void WebRTCIdentityServiceHost::OnComplete(int status, - const std::string& certificate, - const std::string& private_key) { +void WebRTCIdentityServiceHost::OnComplete(int sequence_number, + int status, + const std::string& certificate, + const std::string& private_key) { cancel_callback_.Reset(); if (status == net::OK) { - Send(new WebRTCIdentityHostMsg_IdentityReady(certificate, private_key)); + Send(new WebRTCIdentityHostMsg_IdentityReady( + sequence_number, certificate, private_key)); } else { - SendErrorMessage(status); + SendErrorMessage(sequence_number, status); } } -void WebRTCIdentityServiceHost::SendErrorMessage(int error) { - Send(new WebRTCIdentityHostMsg_RequestFailed(error)); +void WebRTCIdentityServiceHost::SendErrorMessage(int sequence_number, + int error) { + Send(new WebRTCIdentityHostMsg_RequestFailed(sequence_number, error)); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h index 3676223fe86..3a5921c5c2d 100644 --- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h +++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h @@ -37,19 +37,26 @@ class CONTENT_EXPORT WebRTCIdentityServiceHost : public BrowserMessageFilter { bool* message_was_ok) OVERRIDE; private: + // |sequence_number| is the same as in the OnRequestIdentity call. // See WebRTCIdentityStore for the meaning of the parameters. - void OnComplete(int status, + void OnComplete(int sequence_number, + int status, const std::string& certificate, const std::string& private_key); - // See WebRTCIdentityStore for the meaning of the parameters. - void OnRequestIdentity(const GURL& origin, + // |sequence_number| is a renderer wide unique number for each request and + // will be echoed in the response to handle the possibility that the renderer + // cancels the request after the browser sends the response and before it's + // received by the renderer. + // See WebRTCIdentityStore for the meaning of the other parameters. + void OnRequestIdentity(int sequence_number, + const GURL& origin, const std::string& identity_name, const std::string& common_name); void OnCancelRequest(); - void SendErrorMessage(int error); + void SendErrorMessage(int sequence_number, int error); int renderer_process_id_; base::Closure cancel_callback_; diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc index 341378d3c7b..cbedf512bc5 100644 --- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc @@ -24,6 +24,7 @@ static const char FAKE_CERTIFICATE[] = "fake cert"; static const char FAKE_PRIVATE_KEY[] = "fake private key"; static const int FAKE_ERROR = 100; static const int FAKE_RENDERER_ID = 10; +const int FAKE_SEQUENCE_NUMBER = 1; class MockWebRTCIdentityStore : public WebRTCIdentityStore { public: @@ -105,8 +106,10 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test { void SendRequestToHost() { bool ok; host_->OnMessageReceived( - WebRTCIdentityMsg_RequestIdentity( - GURL(FAKE_ORIGIN), FAKE_IDENTITY_NAME, FAKE_COMMON_NAME), + WebRTCIdentityMsg_RequestIdentity(FAKE_SEQUENCE_NUMBER, + GURL(FAKE_ORIGIN), + FAKE_IDENTITY_NAME, + FAKE_COMMON_NAME), &ok); ASSERT_TRUE(ok); } @@ -122,9 +125,10 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test { IPC::Message ipc = host_->GetLastMessage(); EXPECT_EQ(ipc.type(), WebRTCIdentityHostMsg_RequestFailed::ID); - Tuple1<int> error_in_message; + Tuple2<int, int> error_in_message; WebRTCIdentityHostMsg_RequestFailed::Read(&ipc, &error_in_message); - EXPECT_EQ(error, error_in_message.a); + EXPECT_EQ(FAKE_SEQUENCE_NUMBER, error_in_message.a); + EXPECT_EQ(error, error_in_message.b); } void VerifyIdentityReadyMessage(const std::string& cert, @@ -133,10 +137,11 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test { IPC::Message ipc = host_->GetLastMessage(); EXPECT_EQ(ipc.type(), WebRTCIdentityHostMsg_IdentityReady::ID); - Tuple2<std::string, std::string> identity_in_message; + Tuple3<int, std::string, std::string> identity_in_message; WebRTCIdentityHostMsg_IdentityReady::Read(&ipc, &identity_in_message); - EXPECT_EQ(cert, identity_in_message.a); - EXPECT_EQ(key, identity_in_message.b); + EXPECT_EQ(FAKE_SEQUENCE_NUMBER, identity_in_message.a); + EXPECT_EQ(cert, identity_in_message.b); + EXPECT_EQ(key, identity_in_message.c); } protected: diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event.cc index 3cb2af1397b..646b358ac8d 100644 --- a/chromium/content/browser/renderer_host/native_web_keyboard_event.cc +++ b/chromium/content/browser/renderer_host/native_web_keyboard_event.cc @@ -4,7 +4,7 @@ #include "content/public/browser/native_web_keyboard_event.h" -#include "ui/base/events/event_constants.h" +#include "ui/events/event_constants.h" namespace content { diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc index 1658d8d3dc2..ad3aae357cb 100644 --- a/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc +++ b/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "content/browser/renderer_host/web_input_event_aura.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" namespace { @@ -77,7 +77,8 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent( nativeKeyCode = character; text[0] = character; unmodifiedText[0] = character; - isSystemKey = (state & ui::EF_ALT_DOWN) != 0; + isSystemKey = + (state & ui::EF_ALT_DOWN) != 0 && (state & ui::EF_ALTGR_DOWN) == 0; setKeyIdentifierFromWindowsKeyCode(); } diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc index f7631945fec..74ce6a36cbb 100644 --- a/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc +++ b/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc @@ -6,9 +6,7 @@ #include <gdk/gdk.h> -#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" - -using WebKit::WebInputEventFactory; +#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h" namespace { @@ -32,7 +30,7 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent() } NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event) - : WebKeyboardEvent(WebInputEventFactory::keyboardEvent(&native_event->key)), + : WebKeyboardEvent(WebKeyboardEventBuilder::Build(&native_event->key)), skip_in_browser(false), match_edit_command(false) { CopyEventTo(native_event, &os_event); @@ -41,9 +39,9 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event) NativeWebKeyboardEvent::NativeWebKeyboardEvent(wchar_t character, int state, double time_stamp_seconds) - : WebKeyboardEvent(WebInputEventFactory::keyboardEvent(character, - state, - time_stamp_seconds)), + : WebKeyboardEvent(WebKeyboardEventBuilder::Build(character, + state, + time_stamp_seconds)), os_event(NULL), skip_in_browser(false), match_edit_command(false) { diff --git a/chromium/content/browser/renderer_host/overscroll_configuration.cc b/chromium/content/browser/renderer_host/overscroll_configuration.cc index 77585e5fb02..f7c5579f56f 100644 --- a/chromium/content/browser/renderer_host/overscroll_configuration.cc +++ b/chromium/content/browser/renderer_host/overscroll_configuration.cc @@ -11,7 +11,8 @@ namespace { float g_horiz_threshold_complete = 0.25f; float g_vert_threshold_complete = 0.20f; -float g_horiz_threshold_start = 50.f; +float g_horiz_threshold_start_touchscreen = 50.f; +float g_horiz_threshold_start_touchpad = 50.f; float g_vert_threshold_start = 0.f; float g_horiz_resist_after = 30.f; @@ -31,8 +32,12 @@ void SetOverscrollConfig(OverscrollConfig config, float value) { g_vert_threshold_complete = value; break; - case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START: - g_horiz_threshold_start = value; + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN: + g_horiz_threshold_start_touchscreen = value; + break; + + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD: + g_horiz_threshold_start_touchpad = value; break; case OVERSCROLL_CONFIG_VERT_THRESHOLD_START: @@ -61,8 +66,11 @@ float GetOverscrollConfig(OverscrollConfig config) { case OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE: return g_vert_threshold_complete; - case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START: - return g_horiz_threshold_start; + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN: + return g_horiz_threshold_start_touchscreen; + + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD: + return g_horiz_threshold_start_touchpad; case OVERSCROLL_CONFIG_VERT_THRESHOLD_START: return g_vert_threshold_start; diff --git a/chromium/content/browser/renderer_host/overscroll_controller.cc b/chromium/content/browser/renderer_host/overscroll_controller.cc index 3fed4c332a7..f07efab61f4 100644 --- a/chromium/content/browser/renderer_host/overscroll_controller.cc +++ b/chromium/content/browser/renderer_host/overscroll_controller.cc @@ -9,6 +9,8 @@ #include "content/public/browser/overscroll_configuration.h" #include "content/public/browser/render_widget_host_view.h" +using WebKit::WebInputEvent; + namespace content { OverscrollController::OverscrollController( @@ -62,23 +64,25 @@ bool OverscrollController::WillDispatchEvent( // touch-scrolls maintain state in the renderer side (in the compositor, for // example), and the event that completes this action needs to be sent to // the renderer so that those states can be updated/reset appropriately. - // Send the event through the host when appropriate. - if (ShouldForwardToHost(event)) { + if (WebKit::WebInputEvent::isGestureEventType(event.type)) { + // A gesture-event isn't sent to the GestureEventFilter when overscroll is + // in progress. So dispatch the event through the RenderWidgetHost so that + // it can reach the GestureEventFilter. const WebKit::WebGestureEvent& gevent = static_cast<const WebKit::WebGestureEvent&>(event); return render_widget_host_->ShouldForwardGestureEvent( GestureEventWithLatencyInfo(gevent, latency_info)); } - return false; + return true; } if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) { SetOverscrollMode(OVERSCROLL_NONE); - // The overscroll gesture status is being reset. If the event is a - // gesture event (from either touchscreen or trackpad), then make sure the - // host gets the event first (if it didn't already process it). - if (ShouldForwardToHost(event)) { + if (WebKit::WebInputEvent::isGestureEventType(event.type)) { + // A gesture-event isn't sent to the GestureEventFilter when overscroll is + // in progress. So dispatch the event through the RenderWidgetHost so that + // it can reach the GestureEventFilter. const WebKit::WebGestureEvent& gevent = static_cast<const WebKit::WebGestureEvent&>(event); return render_widget_host_->ShouldForwardGestureEvent( @@ -90,16 +94,9 @@ bool OverscrollController::WillDispatchEvent( } if (overscroll_mode_ != OVERSCROLL_NONE) { - // Consume the event and update overscroll state when in the middle of the - // overscroll gesture. - ProcessEventForOverscroll(event); - - if (event.type == WebKit::WebInputEvent::TouchEnd || - event.type == WebKit::WebInputEvent::TouchCancel || - event.type == WebKit::WebInputEvent::TouchMove) { - return true; - } - return false; + // Consume the event only if it updates the overscroll state. + if (ProcessEventForOverscroll(event)) + return false; } return true; @@ -224,24 +221,29 @@ bool OverscrollController::DispatchEventResetsState( } } -void OverscrollController::ProcessEventForOverscroll( +bool OverscrollController::ProcessEventForOverscroll( const WebKit::WebInputEvent& event) { + bool event_processed = false; switch (event.type) { case WebKit::WebInputEvent::MouseWheel: { const WebKit::WebMouseWheelEvent& wheel = static_cast<const WebKit::WebMouseWheelEvent&>(event); if (!wheel.hasPreciseScrollingDeltas) - return; + break; ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX, - wheel.deltaY * wheel.accelerationRatioY); + wheel.deltaY * wheel.accelerationRatioY, + wheel.type); + event_processed = true; break; } case WebKit::WebInputEvent::GestureScrollUpdate: { const WebKit::WebGestureEvent& gesture = static_cast<const WebKit::WebGestureEvent&>(event); ProcessOverscroll(gesture.data.scrollUpdate.deltaX, - gesture.data.scrollUpdate.deltaY); + gesture.data.scrollUpdate.deltaY, + gesture.type); + event_processed = true; break; } case WebKit::WebInputEvent::GestureFlingStart: { @@ -254,12 +256,14 @@ void OverscrollController::ProcessEventForOverscroll( if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) || (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) { CompleteAction(); + event_processed = true; break; } } else if (fabs(velocity_y) > kFlingVelocityThreshold) { if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) || (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) { CompleteAction(); + event_processed = true; break; } } @@ -274,15 +278,20 @@ void OverscrollController::ProcessEventForOverscroll( WebKit::WebInputEvent::isTouchEventType(event.type)) << "Received unexpected event: " << event.type; } + return event_processed; } -void OverscrollController::ProcessOverscroll(float delta_x, float delta_y) { +void OverscrollController::ProcessOverscroll(float delta_x, + float delta_y, + WebKit::WebInputEvent::Type type) { if (scroll_state_ != STATE_CONTENT_SCROLLING) overscroll_delta_x_ += delta_x; overscroll_delta_y_ += delta_y; float horiz_threshold = GetOverscrollConfig( - OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START); + WebInputEvent::isGestureEventType(type) ? + OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN : + OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD); float vert_threshold = GetOverscrollConfig( OVERSCROLL_CONFIG_VERT_THRESHOLD_START); if (fabs(overscroll_delta_x_) <= horiz_threshold && @@ -359,14 +368,4 @@ void OverscrollController::SetOverscrollMode(OverscrollMode mode) { delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_); } -bool OverscrollController::ShouldForwardToHost( - const WebKit::WebInputEvent& event) const { - if (!WebKit::WebInputEvent::isGestureEventType(event.type)) - return false; - - // If the RenderWidgetHost already processed this event, then the event must - // not be sent again. - return !render_widget_host_->HasQueuedGestureEvents(); -} - } // namespace content diff --git a/chromium/content/browser/renderer_host/overscroll_controller.h b/chromium/content/browser/renderer_host/overscroll_controller.h index fc1fc5eb47f..fab531947ee 100644 --- a/chromium/content/browser/renderer_host/overscroll_controller.h +++ b/chromium/content/browser/renderer_host/overscroll_controller.h @@ -89,13 +89,16 @@ class OverscrollController { // overscroll gesture status. bool DispatchEventResetsState(const WebKit::WebInputEvent& event) const; - // Processes an event and updates internal state for overscroll. - void ProcessEventForOverscroll(const WebKit::WebInputEvent& event); + // Processes an event to update the internal state for overscroll. Returns + // true if the state is updated, false otherwise. + bool ProcessEventForOverscroll(const WebKit::WebInputEvent& event); // Processes horizontal overscroll. This can update both the overscroll mode // and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_| // and |overscroll_delta_y_|). - void ProcessOverscroll(float delta_x, float delta_y); + void ProcessOverscroll(float delta_x, + float delta_y, + WebKit::WebInputEvent::Type event_type); // Completes the desired action from the current gesture. void CompleteAction(); @@ -104,10 +107,6 @@ class OverscrollController { // appropriate). void SetOverscrollMode(OverscrollMode new_mode); - // Returns whether the input event should be forwarded to the - // RenderWidgetHost. - bool ShouldForwardToHost(const WebKit::WebInputEvent& event) const; - // The RenderWidgetHost that owns this overscroll controller. RenderWidgetHostImpl* render_widget_host_; diff --git a/chromium/content/browser/renderer_host/p2p/OWNERS b/chromium/content/browser/renderer_host/p2p/OWNERS index 17c3a1e6983..f12c2d60e83 100644 --- a/chromium/content/browser/renderer_host/p2p/OWNERS +++ b/chromium/content/browser/renderer_host/p2p/OWNERS @@ -1,2 +1,3 @@ -sergeyu@chromium.org hclam@chromium.org +mallinath@chromium.org +sergeyu@chromium.org diff --git a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc index f1249d51e89..d1a60b451a8 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc @@ -51,7 +51,9 @@ class P2PSocketDispatcherHost::DnsRequest { net::HostResolver::RequestInfo info(net::HostPortPair(host_name_, 0)); int result = resolver_.Resolve( - info, &addresses_, + info, + net::DEFAULT_PRIORITY, + &addresses_, base::Bind(&P2PSocketDispatcherHost::DnsRequest::OnDone, base::Unretained(this)), net::BoundNetLog()); @@ -190,8 +192,8 @@ void P2PSocketDispatcherHost::OnCreateSocket( return; } - scoped_ptr<P2PSocketHost> socket( - P2PSocketHost::Create(this, socket_id, type, url_context_.get())); + scoped_ptr<P2PSocketHost> socket(P2PSocketHost::Create( + this, socket_id, type, url_context_.get(), &throttler_)); if (!socket) { Send(new P2PMsg_OnError(socket_id)); diff --git a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h index 8a31ae558b1..2369eb2dbea 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h +++ b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h @@ -7,6 +7,7 @@ #include <map> +#include "content/browser/renderer_host/p2p/socket_host_throttler.h" #include "content/common/p2p_sockets.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_thread.h" @@ -84,6 +85,7 @@ class P2PSocketDispatcherHost bool monitoring_networks_; std::set<DnsRequest*> dns_requests_; + P2PMessageThrottler throttler_; DISALLOW_COPY_AND_ASSIGN(P2PSocketDispatcherHost); }; diff --git a/chromium/content/browser/renderer_host/p2p/socket_host.cc b/chromium/content/browser/renderer_host/p2p/socket_host.cc index 027bf2a608b..4b45e99adad 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host.cc @@ -15,7 +15,8 @@ const uint32 kStunMagicCookie = 0x2112A442; namespace content { -P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, int id) +P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, + int id) : message_sender_(message_sender), id_(id), state_(STATE_UNINITIALIZED) { @@ -73,11 +74,11 @@ bool P2PSocketHost::IsRequestOrResponse(StunMessageType type) { // static P2PSocketHost* P2PSocketHost::Create( IPC::Sender* message_sender, int id, P2PSocketType type, - net::URLRequestContextGetter* url_context) { + net::URLRequestContextGetter* url_context, + P2PMessageThrottler* throttler) { switch (type) { case P2P_SOCKET_UDP: - return new P2PSocketHostUdp(message_sender, id); - + return new P2PSocketHostUdp(message_sender, id, throttler); case P2P_SOCKET_TCP_SERVER: return new P2PSocketHostTcpServer( message_sender, id, P2P_SOCKET_TCP_CLIENT); diff --git a/chromium/content/browser/renderer_host/p2p/socket_host.h b/chromium/content/browser/renderer_host/p2p/socket_host.h index 8228300408e..fa186c84b49 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host.h +++ b/chromium/content/browser/renderer_host/p2p/socket_host.h @@ -7,7 +7,6 @@ #include "content/common/content_export.h" #include "content/common/p2p_sockets.h" - #include "net/base/ip_endpoint.h" namespace IPC { @@ -19,6 +18,7 @@ class URLRequestContextGetter; } namespace content { +class P2PMessageThrottler; // Base class for P2P sockets. class CONTENT_EXPORT P2PSocketHost { @@ -27,7 +27,8 @@ class CONTENT_EXPORT P2PSocketHost { // Creates P2PSocketHost of the specific type. static P2PSocketHost* Create(IPC::Sender* message_sender, int id, P2PSocketType type, - net::URLRequestContextGetter* url_context); + net::URLRequestContextGetter* url_context, + P2PMessageThrottler* throttler); virtual ~P2PSocketHost(); @@ -45,6 +46,8 @@ class CONTENT_EXPORT P2PSocketHost { protected: friend class P2PSocketHostTcpTestBase; + // TODO(mallinath) - Remove this below enum and use one defined in + // libjingle/souce/talk/p2p/base/stun.h enum StunMessageType { STUN_BINDING_REQUEST = 0x0001, STUN_BINDING_RESPONSE = 0x0101, @@ -58,7 +61,15 @@ class CONTENT_EXPORT P2PSocketHost { STUN_SEND_REQUEST = 0x0004, STUN_SEND_RESPONSE = 0x0104, STUN_SEND_ERROR_RESPONSE = 0x0114, - STUN_DATA_INDICATION = 0x0115 + STUN_DATA_INDICATION = 0x0115, + TURN_SEND_INDICATION = 0x0016, + TURN_DATA_INDICATION = 0x0017, + TURN_CREATE_PERMISSION_REQUEST = 0x0008, + TURN_CREATE_PERMISSION_RESPONSE = 0x0108, + TURN_CREATE_PERMISSION_ERROR_RESPONSE = 0x0118, + TURN_CHANNEL_BIND_REQUEST = 0x0009, + TURN_CHANNEL_BIND_RESPONSE = 0x0109, + TURN_CHANNEL_BIND_ERROR_RESPONSE = 0x0119, }; enum State { diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc index 8e13bf8f5b5..de9927a7168 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -134,18 +134,23 @@ void P2PSocketHostTcpBase::OnConnected(int result) { if (IsTlsClientSocket(type_)) { state_ = STATE_TLS_CONNECTING; StartTls(); - } else { - if (IsPseudoTlsClientSocket(type_)) { - socket_.reset(new jingle_glue::FakeSSLClientSocket(socket_.release())); + } else if (IsPseudoTlsClientSocket(type_)) { + scoped_ptr<net::StreamSocket> transport_socket = socket_.Pass(); + socket_.reset( + new jingle_glue::FakeSSLClientSocket(transport_socket.Pass())); + state_ = STATE_TLS_CONNECTING; + int status = socket_->Connect( + base::Bind(&P2PSocketHostTcpBase::ProcessTlsSslConnectDone, + base::Unretained(this))); + if (status != net::ERR_IO_PENDING) { + ProcessTlsSslConnectDone(status); } - + } else { // If we are not doing TLS, we are ready to send data now. // In case of TLS, SignalConnect will be sent only after TLS handshake is // successfull. So no buffering will be done at socket handlers if any // packets sent before that by the application. - state_ = STATE_OPEN; - DoSendSocketCreateMsg(); - DoRead(); + OnOpen(); } } @@ -155,7 +160,7 @@ void P2PSocketHostTcpBase::StartTls() { scoped_ptr<net::ClientSocketHandle> socket_handle( new net::ClientSocketHandle()); - socket_handle->set_socket(socket_.release()); + socket_handle->SetSocket(socket_.Pass()); net::SSLClientSocketContext context; context.cert_verifier = url_context_->GetURLRequestContext()->cert_verifier(); @@ -171,24 +176,27 @@ void P2PSocketHostTcpBase::StartTls() { net::ClientSocketFactory::GetDefaultFactory(); DCHECK(socket_factory); - socket_.reset(socket_factory->CreateSSLClientSocket( - socket_handle.release(), dest_host_port_pair, ssl_config, context)); + socket_ = socket_factory->CreateSSLClientSocket( + socket_handle.Pass(), dest_host_port_pair, ssl_config, context); int status = socket_->Connect( - base::Bind(&P2PSocketHostTcpBase::ProcessTlsConnectDone, + base::Bind(&P2PSocketHostTcpBase::ProcessTlsSslConnectDone, base::Unretained(this))); if (status != net::ERR_IO_PENDING) { - ProcessTlsConnectDone(status); + ProcessTlsSslConnectDone(status); } } -void P2PSocketHostTcpBase::ProcessTlsConnectDone(int status) { +void P2PSocketHostTcpBase::ProcessTlsSslConnectDone(int status) { DCHECK_NE(status, net::ERR_IO_PENDING); DCHECK_EQ(state_, STATE_TLS_CONNECTING); if (status != net::OK) { OnError(); return; } + OnOpen(); +} +void P2PSocketHostTcpBase::OnOpen() { state_ = STATE_OPEN; DoSendSocketCreateMsg(); DoRead(); diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h index e5fa08ff93f..c0047a1c26a 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h +++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h @@ -61,7 +61,7 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost { // SSL/TLS connection functions. void StartTls(); - void ProcessTlsConnectDone(int status); + void ProcessTlsSslConnectDone(int status); void DidCompleteRead(int result); void DoRead(); @@ -74,6 +74,8 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost { void OnRead(int result); void OnWritten(int result); + // Helper method to send socket create message and start read. + void OnOpen(); void DoSendSocketCreateMsg(); net::IPEndPoint remote_address_; diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_throttler.cc b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.cc new file mode 100644 index 00000000000..50a4dd0ac83 --- /dev/null +++ b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2013 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/p2p/socket_host_throttler.h" +#include "third_party/libjingle/source/talk/base/ratelimiter.h" +#include "third_party/libjingle/source/talk/base/timing.h" + +namespace content { + +namespace { + +const int kMaxIceMessageBandwidth = 256 * 1024; + +} // namespace + + +P2PMessageThrottler::P2PMessageThrottler() + : timing_(new talk_base::Timing()), + rate_limiter_(new talk_base::RateLimiter(kMaxIceMessageBandwidth, 1.0)) { +} + +P2PMessageThrottler::~P2PMessageThrottler() { +} + +void P2PMessageThrottler::SetTiming(scoped_ptr<talk_base::Timing> timing) { + timing_ = timing.Pass(); +} + +void P2PMessageThrottler::SetSendIceBandwidth(int bandwidth_kbps) { + rate_limiter_.reset(new talk_base::RateLimiter(bandwidth_kbps, 1.0)); +} + +bool P2PMessageThrottler::DropNextPacket(size_t packet_len) { + double now = timing_->TimerNow(); + if (!rate_limiter_->CanUse(packet_len, now)) { + // Exceeding the send rate, this packet should be dropped. + return true; + } + + rate_limiter_->Use(packet_len, now); + return false; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_throttler.h b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.h new file mode 100644 index 00000000000..166d30054f5 --- /dev/null +++ b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.h @@ -0,0 +1,40 @@ +// Copyright (c) 2013 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_P2P_SOCKET_HOST_THROTTLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_THROTTLER_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" + +namespace talk_base { +class RateLimiter; +class Timing; +} + +namespace content { + +// A very simple message throtller. User of this class must drop the packet if +// DropNextPacket returns false for that packet. This method verifies the +// current sendrate against the required sendrate. + +class CONTENT_EXPORT P2PMessageThrottler { + public: + P2PMessageThrottler(); + virtual ~P2PMessageThrottler(); + + void SetTiming(scoped_ptr<talk_base::Timing> timing); + bool DropNextPacket(size_t packet_len); + void SetSendIceBandwidth(int bandwith_kbps); + + private: + scoped_ptr<talk_base::Timing> timing_; + scoped_ptr<talk_base::RateLimiter> rate_limiter_; + + DISALLOW_COPY_AND_ASSIGN(P2PMessageThrottler); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_THROTTLER_H_ diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc index bcfb282a2c4..54c2b4cd4ed 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/debug/trace_event.h" +#include "content/browser/renderer_host/p2p/socket_host_throttler.h" #include "content/common/p2p_messages.h" #include "ipc/ipc_sender.h" #include "net/base/io_buffer.h" @@ -61,11 +62,14 @@ P2PSocketHostUdp::PendingPacket::PendingPacket( P2PSocketHostUdp::PendingPacket::~PendingPacket() { } -P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender, int id) +P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender, + int id, + P2PMessageThrottler* throttler) : P2PSocketHost(message_sender, id), socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())), send_pending_(false), - send_packet_count_(0) { + send_packet_count_(0), + throttler_(throttler) { } P2PSocketHostUdp::~P2PSocketHostUdp() { @@ -180,6 +184,12 @@ void P2PSocketHostUdp::Send(const net::IPEndPoint& to, OnError(); return; } + + if (throttler_->DropNextPacket(data.size())) { + LOG(INFO) << "STUN message is dropped due to high volume."; + // Do not reset socket. + return; + } } if (send_pending_) { diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.h b/chromium/content/browser/renderer_host/p2p/socket_host_udp.h index e9eb36393f4..befac2cae12 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_udp.h +++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.h @@ -21,9 +21,12 @@ namespace content { +class P2PMessageThrottler; + class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost { public: - P2PSocketHostUdp(IPC::Sender* message_sender, int id); + P2PSocketHostUdp(IPC::Sender* message_sender, int id, + P2PMessageThrottler* throttler); virtual ~P2PSocketHostUdp(); // P2PSocketHost overrides. @@ -71,6 +74,7 @@ class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost { // Set of peer for which we have received STUN binding request or // response or relay allocation request or response. ConnectedPeerSet connected_peers_; + P2PMessageThrottler* throttler_; DISALLOW_COPY_AND_ASSIGN(P2PSocketHostUdp); }; diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc index 9ecd56abd54..d42efbe3c35 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc @@ -10,12 +10,14 @@ #include "base/logging.h" #include "base/sys_byteorder.h" #include "content/browser/renderer_host/p2p/socket_host_test_utils.h" +#include "content/browser/renderer_host/p2p/socket_host_throttler.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/udp/datagram_server_socket.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/base/timing.h" using ::testing::_; using ::testing::DeleteArg; @@ -24,6 +26,16 @@ using ::testing::Return; namespace { +class FakeTiming : public talk_base::Timing { + public: + FakeTiming() : now_(0.0) {} + virtual double TimerNow() OVERRIDE { return now_; } + void set_now(double now) { now_ = now; } + + private: + double now_; +}; + class FakeDatagramServerSocket : public net::DatagramServerSocket { public: typedef std::pair<net::IPEndPoint, std::vector<char> > UDPPacket; @@ -161,7 +173,7 @@ class P2PSocketHostUdpTest : public testing::Test { MatchMessage(static_cast<uint32>(P2PMsg_OnSocketCreated::ID)))) .WillOnce(DoAll(DeleteArg<0>(), Return(true))); - socket_host_.reset(new P2PSocketHostUdp(&sender_, 0)); + socket_host_.reset(new P2PSocketHostUdp(&sender_, 0, &throttler_)); socket_ = new FakeDatagramServerSocket(&sent_packets_); socket_host_->socket_.reset(socket_); @@ -170,8 +182,12 @@ class P2PSocketHostUdpTest : public testing::Test { dest1_ = ParseAddress(kTestIpAddress1, kTestPort1); dest2_ = ParseAddress(kTestIpAddress2, kTestPort2); + + scoped_ptr<talk_base::Timing> timing(new FakeTiming()); + throttler_.SetTiming(timing.Pass()); } + P2PMessageThrottler throttler_; std::deque<FakeDatagramServerSocket::UDPPacket> sent_packets_; FakeDatagramServerSocket* socket_; // Owned by |socket_host_|. scoped_ptr<P2PSocketHostUdp> socket_host_; @@ -289,4 +305,62 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunResponseDifferentHost) { socket_host_->Send(dest2_, packet); } +// Verify throttler not allowing unlimited sending of ICE messages to +// any destination. +TEST_F(P2PSocketHostUdpTest, ThrottleAfterLimit) { + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(2) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + throttler_.SetSendIceBandwidth(packet1.size() * 2); + socket_host_->Send(dest1_, packet1); + socket_host_->Send(dest2_, packet1); + + net::IPEndPoint dest3 = ParseAddress(kTestIpAddress1, 2222); + // This packet must be dropped by the throttler. + socket_host_->Send(dest3, packet1); + ASSERT_EQ(sent_packets_.size(), 2U); +} + +// Verify we can send packets to a known destination when ICE throttling is +// active. +TEST_F(P2PSocketHostUdpTest, ThrottleAfterLimitAfterReceive) { + // Receive packet from |dest1_|. + std::vector<char> request_packet; + CreateStunRequest(&request_packet); + + EXPECT_CALL(sender_, Send(MatchPacketMessage(request_packet))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); + socket_->ReceivePacket(dest1_, request_packet); + + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(4) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + throttler_.SetSendIceBandwidth(packet1.size()); + // |dest1_| is known address, throttling will not be applied. + socket_host_->Send(dest1_, packet1); + // Trying to send the packet to dest1_ in the same window. It should go. + socket_host_->Send(dest1_, packet1); + + // Throttler should allow this packet to go through. + socket_host_->Send(dest2_, packet1); + + net::IPEndPoint dest3 = ParseAddress(kTestIpAddress1, 2223); + // This packet will be dropped, as limit only for a single packet. + socket_host_->Send(dest3, packet1); + net::IPEndPoint dest4 = ParseAddress(kTestIpAddress1, 2224); + // This packet should also be dropped. + socket_host_->Send(dest4, packet1); + // |dest1| is known, we can send as many packets to it. + socket_host_->Send(dest1_, packet1); + ASSERT_EQ(sent_packets_.size(), 4U); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/OWNERS b/chromium/content/browser/renderer_host/pepper/OWNERS index dc19a03cb21..33d4c55751a 100644 --- a/chromium/content/browser/renderer_host/pepper/OWNERS +++ b/chromium/content/browser/renderer_host/pepper/OWNERS @@ -1,3 +1,4 @@ +bbudge@chromium.org dmichael@chromium.org raymes@chromium.org yzshen@chromium.org diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc index 10eef8b5515..db3f8eff2ae 100644 --- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc +++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc @@ -4,6 +4,7 @@ #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/pepper_message_filter.h" #include "content/browser/tracing/trace_message_filter.h" #include "content/common/pepper_renderer_instance_data.h" #include "content/public/browser/render_view_host.h" @@ -18,23 +19,17 @@ BrowserPpapiHost* BrowserPpapiHost::CreateExternalPluginProcess( ppapi::PpapiPermissions permissions, base::ProcessHandle plugin_child_process, IPC::ChannelProxy* channel, - net::HostResolver* host_resolver, int render_process_id, int render_view_id, const base::FilePath& profile_directory) { - scoped_refptr<PepperMessageFilter> pepper_message_filter( - new PepperMessageFilter(permissions, - host_resolver, - render_process_id, - render_view_id)); - // The plugin name and path shouldn't be needed for external plugins. BrowserPpapiHostImpl* browser_ppapi_host = new BrowserPpapiHostImpl(sender, permissions, std::string(), - base::FilePath(), profile_directory, true, - pepper_message_filter); + base::FilePath(), profile_directory, true); browser_ppapi_host->set_plugin_process_handle(plugin_child_process); + scoped_refptr<PepperMessageFilter> pepper_message_filter( + new PepperMessageFilter()); channel->AddFilter(pepper_message_filter); channel->AddFilter(browser_ppapi_host->message_filter().get()); channel->AddFilter(new TraceMessageFilter()); @@ -48,17 +43,17 @@ BrowserPpapiHostImpl::BrowserPpapiHostImpl( const std::string& plugin_name, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, - bool external_plugin, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter) + bool external_plugin) : ppapi_host_(new ppapi::host::PpapiHost(sender, permissions)), plugin_process_handle_(base::kNullProcessHandle), plugin_name_(plugin_name), plugin_path_(plugin_path), profile_data_directory_(profile_data_directory), - external_plugin_(external_plugin) { + external_plugin_(external_plugin), + ssl_context_helper_(new SSLContextHelper()) { message_filter_ = new HostMessageFilter(ppapi_host_.get()); ppapi_host_->AddHostFactoryFilter(scoped_ptr<ppapi::host::HostFactory>( - new ContentBrowserPepperHostFactory(this, pepper_message_filter))); + new ContentBrowserPepperHostFactory(this))); } BrowserPpapiHostImpl::~BrowserPpapiHostImpl() { diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h index c46ab13b220..f16d1d82c18 100644 --- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h +++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h @@ -13,7 +13,7 @@ #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" -#include "content/browser/renderer_host/pepper/pepper_message_filter.h" +#include "content/browser/renderer_host/pepper/ssl_context_helper.h" #include "content/common/content_export.h" #include "content/common/pepper_renderer_instance_data.h" #include "content/public/browser/browser_ppapi_host.h" @@ -36,10 +36,7 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost { const std::string& plugin_name, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, - bool external_plugin, - // TODO (ygorshenin@): remove this once TCP sockets are - // converted to the new design. - const scoped_refptr<PepperMessageFilter>& pepper_message_filter); + bool external_plugin); virtual ~BrowserPpapiHostImpl(); // BrowserPpapiHost. @@ -72,6 +69,10 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost { return message_filter_; } + const scoped_refptr<SSLContextHelper>& ssl_context_helper() const { + return ssl_context_helper_; + } + private: friend class BrowserPpapiHostTest; @@ -103,6 +104,8 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost { // BrowserPpapiHost::CreateExternalPluginProcess. bool external_plugin_; + scoped_refptr<SSLContextHelper> ssl_context_helper_; + // Tracks all PP_Instances in this plugin and associated renderer-related // data. typedef std::map<PP_Instance, PepperRendererInstanceData> InstanceMap; diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc index a8c257abacd..c338a990f59 100644 --- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc +++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc @@ -16,8 +16,7 @@ BrowserPpapiHostTest::BrowserPpapiHostTest() std::string(), base::FilePath(), base::FilePath(), - false, - NULL)); + false)); ppapi_host_->set_plugin_process_handle(base::GetCurrentProcessHandle()); } diff --git a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc index f0cefc40396..39e778dfe6b 100644 --- a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc +++ b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc @@ -11,10 +11,12 @@ #include "content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h" #include "content/browser/renderer_host/pepper/pepper_gamepad_host.h" #include "content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h" +#include "content/browser/renderer_host/pepper/pepper_network_monitor_host.h" #include "content/browser/renderer_host/pepper/pepper_network_proxy_host.h" #include "content/browser/renderer_host/pepper/pepper_print_settings_manager.h" #include "content/browser/renderer_host/pepper/pepper_printing_host.h" #include "content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h" +#include "content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h" #include "content/browser/renderer_host/pepper/pepper_truetype_font_list_host.h" #include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h" #include "ppapi/host/message_filter_host.h" @@ -36,18 +38,17 @@ const size_t kMaxSocketsAllowed = 1024; bool CanCreateSocket() { return - PepperUDPSocketMessageFilter::GetNumInstances() + - PepperTCPServerSocketMessageFilter::GetNumInstances() < + PepperTCPServerSocketMessageFilter::GetNumInstances() + + PepperTCPSocketMessageFilter::GetNumInstances() + + PepperUDPSocketMessageFilter::GetNumInstances() < kMaxSocketsAllowed; } } // namespace ContentBrowserPepperHostFactory::ContentBrowserPepperHostFactory( - BrowserPpapiHostImpl* host, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter) - : host_(host), - pepper_message_filter_(pepper_message_filter) { + BrowserPpapiHostImpl* host) + : host_(host) { } ContentBrowserPepperHostFactory::~ContentBrowserPepperHostFactory() { @@ -102,6 +103,15 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(new PepperFileRefHost( host_, instance, params.pp_resource(), file_system, internal_path)); } + case PpapiHostMsg_TCPSocket_Create::ID: { + ppapi::TCPSocketVersion version; + if (!UnpackMessage<PpapiHostMsg_TCPSocket_Create>(message, &version) || + version == ppapi::TCP_SOCKET_VERSION_PRIVATE) { + return scoped_ptr<ResourceHost>(); + } + + return CreateNewTCPSocket(instance, params.pp_resource(), version); + } case PpapiHostMsg_UDPSocket_Create::ID: { if (CanCreateSocket()) { scoped_refptr<ResourceMessageFilter> udp_socket( @@ -155,8 +165,7 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( if (message.type() == PpapiHostMsg_TCPServerSocket_CreatePrivate::ID) { if (CanCreateSocket()) { scoped_refptr<ResourceMessageFilter> tcp_server_socket( - new PepperTCPServerSocketMessageFilter(host_, instance, true, - pepper_message_filter_)); + new PepperTCPServerSocketMessageFilter(this, host_, instance, true)); return scoped_ptr<ResourceHost>(new MessageFilterHost( host_->GetPpapiHost(), instance, params.pp_resource(), tcp_server_socket)); @@ -164,6 +173,10 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(); } } + if (message.type() == PpapiHostMsg_TCPSocket_CreatePrivate::ID) { + return CreateNewTCPSocket(instance, params.pp_resource(), + ppapi::TCP_SOCKET_VERSION_PRIVATE); + } if (message.type() == PpapiHostMsg_UDPSocket_CreatePrivate::ID) { if (CanCreateSocket()) { scoped_refptr<ResourceMessageFilter> udp_socket( @@ -174,6 +187,10 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(); } } + if (message.type() == PpapiHostMsg_NetworkMonitor_Create::ID) { + return scoped_ptr<ResourceHost>( + new PepperNetworkMonitorHost(host_, instance, params.pp_resource())); + } // Flash interfaces. if (GetPermissions().HasPermission(ppapi::PERMISSION_FLASH)) { @@ -191,6 +208,37 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(); } +scoped_ptr<ppapi::host::ResourceHost> +ContentBrowserPepperHostFactory::CreateAcceptedTCPSocket( + PP_Instance instance, + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket) { + if (!CanCreateSocket()) + return scoped_ptr<ResourceHost>(); + scoped_refptr<ResourceMessageFilter> tcp_socket( + new PepperTCPSocketMessageFilter(host_, instance, version, + socket.Pass())); + return scoped_ptr<ResourceHost>(new MessageFilterHost( + host_->GetPpapiHost(), instance, 0, tcp_socket)); +} + +scoped_ptr<ppapi::host::ResourceHost> +ContentBrowserPepperHostFactory::CreateNewTCPSocket( + PP_Instance instance, + PP_Resource resource, + ppapi::TCPSocketVersion version) { + if (!CanCreateSocket()) + return scoped_ptr<ResourceHost>(); + + scoped_refptr<ResourceMessageFilter> tcp_socket( + new PepperTCPSocketMessageFilter(this, host_, instance, version)); + if (!tcp_socket) + return scoped_ptr<ResourceHost>(); + + return scoped_ptr<ResourceHost>(new MessageFilterHost( + host_->GetPpapiHost(), instance, resource, tcp_socket)); +} + const ppapi::PpapiPermissions& ContentBrowserPepperHostFactory::GetPermissions() const { return host_->GetPpapiHost()->permissions(); diff --git a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h index e39ec35d858..04267a32e8a 100644 --- a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h +++ b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h @@ -7,8 +7,11 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" -#include "content/browser/renderer_host/pepper/pepper_message_filter.h" +#include "base/memory/scoped_ptr.h" +#include "net/socket/tcp_socket.h" +#include "ppapi/c/pp_resource.h" #include "ppapi/host/host_factory.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" namespace ppapi { class PpapiPermissions; @@ -21,11 +24,8 @@ class BrowserPpapiHostImpl; class ContentBrowserPepperHostFactory : public ppapi::host::HostFactory { public: // Non-owning pointer to the filter must outlive this class. - ContentBrowserPepperHostFactory( - BrowserPpapiHostImpl* host, - // TODO (ygorshenin@): remove this once TCP sockets are - // converted to the new design. - const scoped_refptr<PepperMessageFilter>& pepper_message_filter); + explicit ContentBrowserPepperHostFactory(BrowserPpapiHostImpl* host); + virtual ~ContentBrowserPepperHostFactory(); virtual scoped_ptr<ppapi::host::ResourceHost> CreateResourceHost( @@ -34,14 +34,24 @@ class ContentBrowserPepperHostFactory : public ppapi::host::HostFactory { PP_Instance instance, const IPC::Message& message) OVERRIDE; + // Creates ResourceHost for already accepted TCP |socket|. In the case of + // failure returns wrapped NULL. + scoped_ptr<ppapi::host::ResourceHost> CreateAcceptedTCPSocket( + PP_Instance instance, + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket); + private: + scoped_ptr<ppapi::host::ResourceHost> CreateNewTCPSocket( + PP_Instance instance, + PP_Resource resource, + ppapi::TCPSocketVersion version); + const ppapi::PpapiPermissions& GetPermissions() const; // Non-owning pointer. BrowserPpapiHostImpl* host_; - scoped_refptr<PepperMessageFilter> pepper_message_filter_; - DISALLOW_COPY_AND_ASSIGN(ContentBrowserPepperHostFactory); }; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc index 27ba37dfcca..096b908266f 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc @@ -90,21 +90,16 @@ int32_t PepperExternalFileRefBackend::GetAbsolutePath( ppapi::host::ReplyMessageContext reply_context) { host_->SendReply(reply_context, PpapiPluginMsg_FileRef_GetAbsolutePathReply(path_.AsUTF8Unsafe())); - return PP_OK; + + // Use PP_OK_COMPLETIONPENDING instead of PP_OK since we've already sent our + // reply above. + return PP_OK_COMPLETIONPENDING; } fileapi::FileSystemURL PepperExternalFileRefBackend::GetFileSystemURL() const { return fileapi::FileSystemURL(); } -std::string PepperExternalFileRefBackend::GetFileSystemURLSpec() const { - return std::string(); -} - -base::FilePath PepperExternalFileRefBackend::GetExternalPath() const { - return path_; -} - int32_t PepperExternalFileRefBackend::CanRead() const { if (!ChildProcessSecurityPolicyImpl::GetInstance()-> CanReadFile(render_process_id_, path_)) { diff --git a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h index 994e9c291cd..cbfaf6ed511 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h @@ -41,8 +41,6 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend { virtual int32_t GetAbsolutePath(ppapi::host::ReplyMessageContext context) OVERRIDE; virtual fileapi::FileSystemURL GetFileSystemURL() const OVERRIDE; - virtual std::string GetFileSystemURLSpec() const OVERRIDE; - virtual base::FilePath GetExternalPath() const OVERRIDE; virtual int32_t CanRead() const OVERRIDE; virtual int32_t CanWrite() const OVERRIDE; @@ -68,6 +66,8 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend { scoped_refptr<base::TaskRunner> task_runner_; base::WeakPtrFactory<PepperExternalFileRefBackend> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperExternalFileRefBackend); }; } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc index 5370af9d853..c38f8826534 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc @@ -62,9 +62,9 @@ PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host, } fs_type_ = fs_host->GetType(); - // TODO(teravest): Add support for isolated filesystems. if ((fs_type_ != PP_FILESYSTEMTYPE_LOCALPERSISTENT) && - (fs_type_ != PP_FILESYSTEMTYPE_LOCALTEMPORARY)) { + (fs_type_ != PP_FILESYSTEMTYPE_LOCALTEMPORARY) && + (fs_type_ != PP_FILESYSTEMTYPE_ISOLATED)) { DLOG(ERROR) << "Unsupported filesystem type: " << fs_type_; return; } @@ -116,18 +116,6 @@ fileapi::FileSystemURL PepperFileRefHost::GetFileSystemURL() const { return fileapi::FileSystemURL(); } -std::string PepperFileRefHost::GetFileSystemURLSpec() const { - if (backend_) - return backend_->GetFileSystemURLSpec(); - return std::string(); -} - -base::FilePath PepperFileRefHost::GetExternalPath() const { - if (backend_) - return backend_->GetExternalPath(); - return base::FilePath(); -} - int32_t PepperFileRefHost::CanRead() const { if (backend_) return backend_->CanRead(); diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h index 5d765764326..b97ff331ac0 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h @@ -42,8 +42,6 @@ class PepperFileRefBackend { virtual int32_t GetAbsolutePath( ppapi::host::ReplyMessageContext context) = 0; virtual fileapi::FileSystemURL GetFileSystemURL() const = 0; - virtual std::string GetFileSystemURLSpec() const = 0; - virtual base::FilePath GetExternalPath() const = 0; // Returns an error from the pp_errors.h enum. virtual int32_t CanRead() const = 0; @@ -79,10 +77,6 @@ class CONTENT_EXPORT PepperFileRefHost PP_FileSystemType GetFileSystemType() const; fileapi::FileSystemURL GetFileSystemURL() const; - // Required to support FileIO. - std::string GetFileSystemURLSpec() const; - base::FilePath GetExternalPath() const; - int32_t CanRead() const; int32_t CanWrite() const; int32_t CanCreate() const; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc index 1a10da77b83..fce371d8f99 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc @@ -58,16 +58,17 @@ PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host, PP_FileSystemType type) : ResourceHost(host->GetPpapiHost(), instance, resource), browser_ppapi_host_(host), - weak_factory_(this), type_(type), opened_(false), fs_context_(NULL), - called_open_(false) { + called_open_(false), + weak_factory_(this) { } PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() { - if (fs_context_.get()) - fs_context_->operation_runner()->Shutdown(); + // TODO(teravest): Create a FileSystemOperationRunner + // per-PepperFileSystemBrowserHost, force users of this FileSystem to use it, + // and call Shutdown() on it here. } int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived( @@ -151,6 +152,18 @@ void PepperFileSystemBrowserHost::GotFileSystemContext( fs_context_ = fs_context; } +void PepperFileSystemBrowserHost::GotIsolatedFileSystemContext( + ppapi::host::ReplyMessageContext reply_context, + scoped_refptr<fileapi::FileSystemContext> fs_context) { + fs_context_ = fs_context; + if (fs_context.get()) + reply_context.params.set_result(PP_OK); + else + reply_context.params.set_result(PP_ERROR_FAILED); + host()->SendReply(reply_context, + PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply()); +} + void PepperFileSystemBrowserHost::OpenFileSystemComplete( ppapi::host::ReplyMessageContext reply_context, base::PlatformFileError error, @@ -177,7 +190,22 @@ int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem( root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString( url.GetOrigin(), fsid, "crxfs")); opened_ = true; - return PP_OK; + + int render_process_id = 0; + int unused; + if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(), + &render_process_id, + &unused)) { + return PP_ERROR_FAILED; + } + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, + FROM_HERE, + base::Bind(&GetFileSystemContextFromRenderId, render_process_id), + base::Bind(&PepperFileSystemBrowserHost::GotIsolatedFileSystemContext, + weak_factory_.GetWeakPtr(), + context->MakeReplyMessageContext())); + return PP_OK_COMPLETIONPENDING; } } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h index 3eb146e5b7e..ed648fb9938 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h @@ -47,6 +47,9 @@ class PepperFileSystemBrowserHost : ppapi::host::ReplyMessageContext reply_context, fileapi::FileSystemType file_system_type, scoped_refptr<fileapi::FileSystemContext> fs_context); + void GotIsolatedFileSystemContext( + ppapi::host::ReplyMessageContext reply_context, + scoped_refptr<fileapi::FileSystemContext> fs_context); void OpenFileSystemComplete( ppapi::host::ReplyMessageContext reply_context, base::PlatformFileError error, @@ -60,7 +63,6 @@ class PepperFileSystemBrowserHost : const std::string& fsid); BrowserPpapiHost* browser_ppapi_host_; - base::WeakPtrFactory<PepperFileSystemBrowserHost> weak_factory_; PP_FileSystemType type_; bool opened_; // whether open is successful. @@ -68,6 +70,8 @@ class PepperFileSystemBrowserHost : scoped_refptr<fileapi::FileSystemContext> fs_context_; bool called_open_; // whether open has been called. + base::WeakPtrFactory<PepperFileSystemBrowserHost> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(PepperFileSystemBrowserHost); }; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc index e0e17b367ed..bdbdf871e27 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc @@ -132,7 +132,7 @@ int32_t PepperHostResolverMessageFilter::OnMsgResolve( if (!render_view_host || !pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, + &request, render_view_host)) { return PP_ERROR_NOACCESS; } @@ -179,6 +179,7 @@ void PepperHostResolverMessageFilter::DoResolve( new PepperLookupRequest<ReplyMessageContext>( host_resolver, request_info, + net::DEFAULT_PRIORITY, bound_info.release(), base::Bind(&PepperHostResolverMessageFilter::OnLookupFinished, this)); lookup_request->Start(); diff --git a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc index 337807d34e1..3b8c801ec1a 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc @@ -63,27 +63,16 @@ fileapi::FileSystemURL PepperInternalFileRefBackend::GetFileSystemURL() const { if (!fs_url_.is_valid() && fs_host_.get() && fs_host_->IsOpened()) { GURL fs_path = fs_host_->GetRootUrl().Resolve( net::EscapePath(path_.substr(1))); - fs_url_ = GetFileSystemContext()->CrackURL(fs_path); + scoped_refptr<fileapi::FileSystemContext> fs_context = + GetFileSystemContext(); + if (fs_context.get()) + fs_url_ = fs_context->CrackURL(fs_path); } return fs_url_; } -std::string PepperInternalFileRefBackend::GetFileSystemURLSpec() const { - if (fs_host_.get() && fs_host_->IsOpened() && - fs_host_->GetRootUrl().is_valid()) { - return fs_host_->GetRootUrl().Resolve( - net::EscapePath(path_.substr(1))).spec(); - } - return std::string(); -} - -base::FilePath PepperInternalFileRefBackend::GetExternalPath() const { - return base::FilePath(); -} - scoped_refptr<fileapi::FileSystemContext> PepperInternalFileRefBackend::GetFileSystemContext() const { - // TODO(teravest): Make this work for CRX file systems. if (!fs_host_.get()) return NULL; return fs_host_->GetFileSystemContext(); @@ -221,7 +210,7 @@ void PepperInternalFileRefBackend::ReadDirectoryComplete( context.params.set_result(ppapi::PlatformFileErrorToPepperError(error)); - std::vector<ppapi::FileRef_CreateInfo> infos; + std::vector<ppapi::FileRefCreateInfo> infos; std::vector<PP_FileType> file_types; if (error == base::PLATFORM_FILE_OK && fs_host_.get()) { std::string dir_path = path_; @@ -235,7 +224,7 @@ void PepperInternalFileRefBackend::ReadDirectoryComplete( else file_types.push_back(PP_FILETYPE_REGULAR); - ppapi::FileRef_CreateInfo info; + ppapi::FileRefCreateInfo info; info.file_system_type = fs_type_; info.file_system_plugin_resource = fs_host_->pp_resource(); std::string path = diff --git a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h index fb8a6cf7603..81d6ab624cd 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h @@ -46,8 +46,6 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend { OVERRIDE; virtual fileapi::FileSystemURL GetFileSystemURL() const OVERRIDE; - virtual std::string GetFileSystemURLSpec() const OVERRIDE; - virtual base::FilePath GetExternalPath() const OVERRIDE; virtual int32_t CanRead() const OVERRIDE; virtual int32_t CanWrite() const OVERRIDE; @@ -82,6 +80,8 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend { mutable fileapi::FileSystemURL fs_url_; base::WeakPtrFactory<PepperInternalFileRefBackend> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperInternalFileRefBackend); }; } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h b/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h index bcb9a60203f..da116fd640c 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h @@ -24,20 +24,23 @@ class PepperLookupRequest { // callback, when lookup will finish. PepperLookupRequest(net::HostResolver* resolver, const net::HostResolver::RequestInfo& request_info, + net::RequestPriority priority, T* bound_info, const LookupRequestCallback& callback) : resolver_(resolver), request_info_(request_info), + priority_(priority), bound_info_(bound_info), - callback_(callback) { - } + callback_(callback) {} void Start() { - int result = resolver_.Resolve( - request_info_, &addresses_, - base::Bind(&PepperLookupRequest<T>::OnLookupFinished, - base::Unretained(this)), - net::BoundNetLog()); + int result = + resolver_.Resolve(request_info_, + priority_, + &addresses_, + base::Bind(&PepperLookupRequest<T>::OnLookupFinished, + base::Unretained(this)), + net::BoundNetLog()); if (result != net::ERR_IO_PENDING) OnLookupFinished(result); } @@ -50,6 +53,7 @@ class PepperLookupRequest { net::SingleRequestHostResolver resolver_; net::HostResolver::RequestInfo request_info_; + net::RequestPriority priority_; scoped_ptr<T> bound_info_; LookupRequestCallback callback_; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc index 717de350dc7..6434729b734 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc @@ -4,118 +4,20 @@ #include "content/browser/renderer_host/pepper/pepper_message_filter.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/files/file_path.h" #include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/sequenced_worker_pool.h" -#include "base/threading/worker_pool.h" -#include "build/build_config.h" #include "content/browser/renderer_host/pepper/pepper_socket_utils.h" -#include "content/browser/renderer_host/pepper/pepper_tcp_socket.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/renderer_host/render_view_host_impl.h" -#include "content/common/pepper_messages.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/browser/resource_context.h" #include "content/public/common/content_client.h" -#include "net/base/address_family.h" -#include "net/base/address_list.h" -#include "net/base/host_port_pair.h" -#include "net/base/sys_addrinfo.h" -#include "net/cert/cert_verifier.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/shared_impl/api_id.h" -#include "ppapi/shared_impl/private/net_address_private_impl.h" -#include "ppapi/shared_impl/socket_option_data.h" - -using ppapi::NetAddressPrivateImpl; namespace content { -namespace { - -const size_t kMaxSocketsAllowed = 1024; -const uint32 kInvalidSocketID = 0; - -} // namespace -PepperMessageFilter::PepperMessageFilter(int process_id, - BrowserContext* browser_context) - : plugin_type_(PLUGIN_TYPE_IN_PROCESS), - permissions_(), - process_id_(process_id), - external_plugin_render_view_id_(0), - resource_context_(browser_context->GetResourceContext()), - host_resolver_(NULL), - next_socket_id_(1) { - DCHECK(browser_context); - // Keep BrowserContext data in FILE-thread friendly storage. - browser_path_ = browser_context->GetPath(); - incognito_ = browser_context->IsOffTheRecord(); - DCHECK(resource_context_); -} - -PepperMessageFilter::PepperMessageFilter( - const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver) - : plugin_type_(PLUGIN_TYPE_OUT_OF_PROCESS), - permissions_(permissions), - process_id_(0), - external_plugin_render_view_id_(0), - resource_context_(NULL), - host_resolver_(host_resolver), - next_socket_id_(1), - incognito_(false) { - DCHECK(host_resolver); -} - -PepperMessageFilter::PepperMessageFilter( - const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver, - int process_id, - int render_view_id) - : plugin_type_(PLUGIN_TYPE_EXTERNAL_PLUGIN), - permissions_(permissions), - process_id_(process_id), - external_plugin_render_view_id_(render_view_id), - resource_context_(NULL), - host_resolver_(host_resolver), - next_socket_id_(1) { - DCHECK(host_resolver); -} +PepperMessageFilter::PepperMessageFilter() {} +PepperMessageFilter::~PepperMessageFilter() {} bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg, bool* message_was_ok) { bool handled = true; IPC_BEGIN_MESSAGE_MAP_EX(PepperMessageFilter, msg, *message_was_ok) - // TCP messages. - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Create, OnTCPCreate) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_CreatePrivate, - OnTCPCreatePrivate) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Connect, OnTCPConnect) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress, - OnTCPConnectWithNetAddress) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_SSLHandshake, - OnTCPSSLHandshake) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Read, OnTCPRead) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Write, OnTCPWrite) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Disconnect, OnTCPDisconnect) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_SetOption, OnTCPSetOption) - - // NetworkMonitor messages. - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBNetworkMonitor_Start, - OnNetworkMonitorStart) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBNetworkMonitor_Stop, - OnNetworkMonitorStop) - // X509 certificate messages. IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBX509Certificate_ParseDER, OnX509CertificateParseDER); @@ -125,367 +27,14 @@ bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg, return handled; } -void PepperMessageFilter::OnIPAddressChanged() { - GetAndSendNetworkList(); -} - -net::HostResolver* PepperMessageFilter::GetHostResolver() { - return resource_context_ ? - resource_context_->GetHostResolver() : host_resolver_; -} - -net::CertVerifier* PepperMessageFilter::GetCertVerifier() { - if (!cert_verifier_) - cert_verifier_.reset(net::CertVerifier::CreateDefault()); - - return cert_verifier_.get(); -} - -net::TransportSecurityState* PepperMessageFilter::GetTransportSecurityState() { - if (!transport_security_state_) - transport_security_state_.reset(new net::TransportSecurityState); - - return transport_security_state_.get(); -} - -uint32 PepperMessageFilter::AddAcceptedTCPSocket( - int32 routing_id, - uint32 plugin_dispatcher_id, - net::StreamSocket* socket) { - scoped_ptr<net::StreamSocket> s(socket); - - uint32 tcp_socket_id = GenerateSocketID(); - if (tcp_socket_id != kInvalidSocketID) { - // Currently all TCP sockets created this way correspond to - // PPB_TCPSocket_Private. - tcp_sockets_[tcp_socket_id] = linked_ptr<PepperTCPSocket>( - new PepperTCPSocket(this, - routing_id, - plugin_dispatcher_id, - tcp_socket_id, - s.release(), - true /* private_api */)); - } - return tcp_socket_id; -} - -PepperMessageFilter::~PepperMessageFilter() { - if (!network_monitor_ids_.empty()) - net::NetworkChangeNotifier::RemoveIPAddressObserver(this); -} - -void PepperMessageFilter::OnTCPCreate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id) { - CreateTCPSocket(routing_id, plugin_dispatcher_id, false, socket_id); -} - -void PepperMessageFilter::OnTCPCreatePrivate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id) { - CreateTCPSocket(routing_id, plugin_dispatcher_id, true, socket_id); -} - -void PepperMessageFilter::OnTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - // This is only supported by PPB_TCPSocket_Private. - if (!iter->second->private_api()) { - NOTREACHED(); - return; - } - - content::SocketPermissionRequest params( - content::SocketPermissionRequest::TCP_CONNECT, host, port); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::UI, FROM_HERE, - base::Bind(&PepperMessageFilter::CanUseSocketAPIs, this, - routing_id, params, true /* private_api */), - base::Bind(&PepperMessageFilter::DoTCPConnect, this, - routing_id, socket_id, host, port)); -} - -void PepperMessageFilter::DoTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port, - bool allowed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - // Due to current permission check process (IO -> UI -> IO) some - // calls to the TCP socket interface can be intermixed (like - // Connect and Close). So, NOTREACHED() is not appropriate here. - return; - } - - if (routing_id == iter->second->routing_id() && allowed) - iter->second->Connect(host, port); - else - iter->second->SendConnectACKError(PP_ERROR_NOACCESS); -} - -void PepperMessageFilter::OnTCPConnectWithNetAddress( - int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - content::SocketPermissionRequest params = - pepper_socket_utils::CreateSocketPermissionRequest( - content::SocketPermissionRequest::TCP_CONNECT, net_addr); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::UI, FROM_HERE, - base::Bind(&PepperMessageFilter::CanUseSocketAPIs, this, - routing_id, params, iter->second->private_api()), - base::Bind(&PepperMessageFilter::DoTCPConnectWithNetAddress, this, - routing_id, socket_id, net_addr)); -} - -void PepperMessageFilter::DoTCPConnectWithNetAddress( - int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr, - bool allowed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - // Due to current permission check process (IO -> UI -> IO) some - // calls to the TCP socket interface can be intermixed (like - // ConnectWithNetAddress and Close). So, NOTREACHED() is not - // appropriate here. - return; - } - - if (routing_id == iter->second->routing_id() && allowed) - iter->second->ConnectWithNetAddress(net_addr); - else - iter->second->SendConnectACKError(PP_ERROR_NOACCESS); -} - -void PepperMessageFilter::OnTCPSSLHandshake( - uint32 socket_id, - const std::string& server_name, - uint16_t server_port, - const std::vector<std::vector<char> >& trusted_certs, - const std::vector<std::vector<char> >& untrusted_certs) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - // This is only supported by PPB_TCPSocket_Private. - if (!iter->second->private_api()) { - NOTREACHED(); - return; - } - - iter->second->SSLHandshake(server_name, server_port, trusted_certs, - untrusted_certs); -} - -void PepperMessageFilter::OnTCPRead(uint32 socket_id, int32_t bytes_to_read) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - iter->second->Read(bytes_to_read); -} - -void PepperMessageFilter::OnTCPWrite(uint32 socket_id, - const std::string& data) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - iter->second->Write(data); -} - -void PepperMessageFilter::OnTCPDisconnect(uint32 socket_id) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - // Destroying the TCPSocket instance will cancel any pending completion - // callback. From this point on, there won't be any messages associated with - // this socket sent to the plugin side. - tcp_sockets_.erase(iter); -} - -void PepperMessageFilter::OnTCPSetOption(uint32 socket_id, - PP_TCPSocket_Option name, - const ppapi::SocketOptionData& value) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - iter->second->SetOption(name, value); -} - -void PepperMessageFilter::OnNetworkMonitorStart(uint32 plugin_dispatcher_id) { - // Support all in-process plugins, and ones with "private" permissions. - if (plugin_type_ != PLUGIN_TYPE_IN_PROCESS && - !permissions_.HasPermission(ppapi::PERMISSION_PRIVATE)) { - return; - } - - if (network_monitor_ids_.empty()) - net::NetworkChangeNotifier::AddIPAddressObserver(this); - - network_monitor_ids_.insert(plugin_dispatcher_id); - GetAndSendNetworkList(); -} - -void PepperMessageFilter::OnNetworkMonitorStop(uint32 plugin_dispatcher_id) { - // Support all in-process plugins, and ones with "private" permissions. - if (plugin_type_ != PLUGIN_TYPE_IN_PROCESS && - !permissions_.HasPermission(ppapi::PERMISSION_PRIVATE)) { - return; - } - - network_monitor_ids_.erase(plugin_dispatcher_id); - if (network_monitor_ids_.empty()) - net::NetworkChangeNotifier::RemoveIPAddressObserver(this); -} - void PepperMessageFilter::OnX509CertificateParseDER( const std::vector<char>& der, bool* succeeded, ppapi::PPB_X509Certificate_Fields* result) { if (der.size() == 0) *succeeded = false; - *succeeded = PepperTCPSocket::GetCertificateFields(&der[0], der.size(), - result); -} - -uint32 PepperMessageFilter::GenerateSocketID() { - // TODO(yzshen): Change to use Pepper resource ID as socket ID. - // - // Generate a socket ID. For each process which sends us socket requests, IDs - // of living sockets must be unique, to each socket type. - // - // However, it is safe to generate IDs based on the internal state of a single - // PepperSocketMessageHandler object, because for each plugin or renderer - // process, there is at most one PepperMessageFilter (in other words, at most - // one PepperSocketMessageHandler) talking to it. - if (tcp_sockets_.size() >= kMaxSocketsAllowed) - return kInvalidSocketID; - - uint32 socket_id = kInvalidSocketID; - do { - // Although it is unlikely, make sure that we won't cause any trouble when - // the counter overflows. - socket_id = next_socket_id_++; - } while (socket_id == kInvalidSocketID || - tcp_sockets_.find(socket_id) != tcp_sockets_.end()); - - return socket_id; -} - -bool PepperMessageFilter::CanUseSocketAPIs( - int32 render_id, - const content::SocketPermissionRequest& params, - bool private_api) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // External plugins always get their own PepperMessageFilter, initialized with - // a render view id. Use this instead of the one that came with the message, - // which is actually an API ID. - bool external_plugin = false; - if (plugin_type_ == PLUGIN_TYPE_EXTERNAL_PLUGIN) { - external_plugin = true; - render_id = external_plugin_render_view_id_; - } - - RenderViewHostImpl* render_view_host = - RenderViewHostImpl::FromID(process_id_, render_id); - - return pepper_socket_utils::CanUseSocketAPIs(external_plugin, - private_api, - params, - render_view_host); -} - -void PepperMessageFilter::GetAndSendNetworkList() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - BrowserThread::PostBlockingPoolTask( - FROM_HERE, base::Bind(&PepperMessageFilter::DoGetNetworkList, this)); -} - -void PepperMessageFilter::DoGetNetworkList() { - scoped_ptr<net::NetworkInterfaceList> list(new net::NetworkInterfaceList()); - net::GetNetworkList(list.get()); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&PepperMessageFilter::SendNetworkList, - this, base::Passed(&list))); -} - -void PepperMessageFilter::SendNetworkList( - scoped_ptr<net::NetworkInterfaceList> list) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - scoped_ptr< ::ppapi::NetworkList> list_copy( - new ::ppapi::NetworkList(list->size())); - for (size_t i = 0; i < list->size(); ++i) { - const net::NetworkInterface& network = list->at(i); - ::ppapi::NetworkInfo& network_copy = list_copy->at(i); - network_copy.name = network.name; - - network_copy.addresses.resize(1, NetAddressPrivateImpl::kInvalidNetAddress); - bool result = NetAddressPrivateImpl::IPEndPointToNetAddress( - network.address, 0, &(network_copy.addresses[0])); - DCHECK(result); - - // TODO(sergeyu): Currently net::NetworkInterfaceList provides - // only name and one IP address. Add all other fields and copy - // them here. - network_copy.type = PP_NETWORKLIST_UNKNOWN; - network_copy.state = PP_NETWORKLIST_UP; - network_copy.display_name = network.name; - network_copy.mtu = 0; - } - for (NetworkMonitorIdSet::iterator it = network_monitor_ids_.begin(); - it != network_monitor_ids_.end(); ++it) { - Send(new PpapiMsg_PPBNetworkMonitor_NetworkList( - ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE, *it, *list_copy)); - } -} - -void PepperMessageFilter::CreateTCPSocket(int32 routing_id, - uint32 plugin_dispatcher_id, - bool private_api, - uint32* socket_id) { - *socket_id = GenerateSocketID(); - if (*socket_id == kInvalidSocketID) - return; - - tcp_sockets_[*socket_id] = linked_ptr<PepperTCPSocket>( - new PepperTCPSocket(this, routing_id, plugin_dispatcher_id, *socket_id, - private_api)); + *succeeded = + pepper_socket_utils::GetCertificateFields(&der[0], der.size(), result); } } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h index 11c8fbd4902..3f5010ed40e 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h @@ -5,223 +5,35 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_MESSAGE_FILTER_H_ -#include <map> -#include <string> #include <vector> #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" -#include "base/process/process.h" #include "content/public/browser/browser_message_filter.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/common/process_type.h" -#include "net/base/net_util.h" -#include "net/base/network_change_notifier.h" -#include "net/http/transport_security_state.h" -#include "net/socket/stream_socket.h" -#include "net/ssl/ssl_config_service.h" -#include "ppapi/c/pp_resource.h" -#include "ppapi/c/pp_stdint.h" -#include "ppapi/c/ppb_tcp_socket.h" -#include "ppapi/c/private/ppb_flash.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/shared_impl/ppapi_permissions.h" - -struct PP_NetAddress_Private; - -namespace base { -class ListValue; -} - -namespace net { -class CertVerifier; -class HostResolver; -} namespace ppapi { class PPB_X509Certificate_Fields; -class SocketOptionData; } namespace content { -class BrowserContext; -class PepperTCPSocket; -class ResourceContext; -// This class is used in two contexts, both supporting PPAPI plugins. The first -// is on the renderer->browser channel, to handle requests from in-process -// PPAPI plugins and any requests that the PPAPI implementation code in the -// renderer needs to make. The second is on the plugin->browser channel to -// handle requests that out-of-process plugins send directly to the browser. -class PepperMessageFilter - : public BrowserMessageFilter, - public net::NetworkChangeNotifier::IPAddressObserver { +// Message filter that handles IPC for PPB_X509Certificate_Private. +class PepperMessageFilter : public BrowserMessageFilter { public: - // Constructor when used in the context of a render process. - PepperMessageFilter(int process_id, - BrowserContext* browser_context); - - // Constructor when used in the context of a PPAPI process.. - PepperMessageFilter(const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver); - - // Constructor when used in the context of an external plugin, i.e. created by - // the embedder using BrowserPpapiHost::CreateExternalPluginProcess. - PepperMessageFilter(const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver, - int process_id, - int render_view_id); + PepperMessageFilter(); // BrowserMessageFilter methods. virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok) OVERRIDE; - // net::NetworkChangeNotifier::IPAddressObserver interface. - virtual void OnIPAddressChanged() OVERRIDE; - - // Returns the host resolver (it may come from the resource context or the - // host_resolver_ member). - net::HostResolver* GetHostResolver(); - - net::CertVerifier* GetCertVerifier(); - net::TransportSecurityState* GetTransportSecurityState(); - - // Adds already accepted socket to the internal TCP sockets table. Takes - // ownership over |socket|. In the case of failure (full socket table) - // returns 0 and deletes |socket|. Otherwise, returns generated ID for - // |socket|. - uint32 AddAcceptedTCPSocket(int32 routing_id, - uint32 plugin_dispatcher_id, - net::StreamSocket* socket); - - const net::SSLConfig& ssl_config() { return ssl_config_; } - protected: virtual ~PepperMessageFilter(); private: - struct OnConnectTcpBoundInfo { - int routing_id; - int request_id; - }; - - // Containers for sockets keyed by socked_id. - typedef std::map<uint32, linked_ptr<PepperTCPSocket> > TCPSocketMap; - - // Set of disptachers ID's that have subscribed for NetworkMonitor - // notifications. - typedef std::set<uint32> NetworkMonitorIdSet; - - void OnGetLocalTimeZoneOffset(base::Time t, double* result); - - void OnTCPCreate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id); - void OnTCPCreatePrivate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id); - void OnTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port); - void OnTCPConnectWithNetAddress(int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr); - void OnTCPSSLHandshake( - uint32 socket_id, - const std::string& server_name, - uint16_t server_port, - const std::vector<std::vector<char> >& trusted_certs, - const std::vector<std::vector<char> >& untrusted_certs); - void OnTCPRead(uint32 socket_id, int32_t bytes_to_read); - void OnTCPWrite(uint32 socket_id, const std::string& data); - void OnTCPDisconnect(uint32 socket_id); - void OnTCPSetOption(uint32 socket_id, - PP_TCPSocket_Option name, - const ppapi::SocketOptionData& value); - - void OnNetworkMonitorStart(uint32 plugin_dispatcher_id); - void OnNetworkMonitorStop(uint32 plugin_dispatcher_id); - - void DoTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port, - bool allowed); - void DoTCPConnectWithNetAddress(int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr, - bool allowed); void OnX509CertificateParseDER(const std::vector<char>& der, bool* succeeded, ppapi::PPB_X509Certificate_Fields* result); - void OnUpdateActivity(); - - uint32 GenerateSocketID(); - - // Return true if render with given ID can use socket APIs. - bool CanUseSocketAPIs(int32 render_id, - const content::SocketPermissionRequest& params, - bool private_api); - - void GetAndSendNetworkList(); - void DoGetNetworkList(); - void SendNetworkList(scoped_ptr<net::NetworkInterfaceList> list); - void CreateTCPSocket(int32 routing_id, - uint32 plugin_dispatcher_id, - bool private_api, - uint32* socket_id); - enum PluginType { - PLUGIN_TYPE_IN_PROCESS, - PLUGIN_TYPE_OUT_OF_PROCESS, - // External plugin means it was created through - // BrowserPpapiHost::CreateExternalPluginProcess. - PLUGIN_TYPE_EXTERNAL_PLUGIN, - }; - - PluginType plugin_type_; - - // When attached to an out-of-process plugin (be it native or NaCl) this - // will have the Pepper permissions for the plugin. When attached to the - // renderer channel, this will have no permissions listed (since there may - // be many plugins sharing this channel). - ppapi::PpapiPermissions permissions_; - - // Render process ID. - int process_id_; - - // External plugin RenderView id to determine private API access. Normally, we - // handle messages coming from multiple RenderViews, but external plugins - // always creates a new PepperMessageFilter for each RenderView. - int external_plugin_render_view_id_; - - // When non-NULL, this should be used instead of the host_resolver_. - ResourceContext* const resource_context_; - - // When non-NULL, this should be used instead of the resource_context_. Use - // GetHostResolver instead of accessing directly. - net::HostResolver* host_resolver_; - - // The default SSL configuration settings are used, as opposed to Chrome's SSL - // settings. - net::SSLConfig ssl_config_; - // This is lazily created. Users should use GetCertVerifier to retrieve it. - scoped_ptr<net::CertVerifier> cert_verifier_; - // This is lazily created. Users should use GetTransportSecurityState to - // retrieve it. - scoped_ptr<net::TransportSecurityState> transport_security_state_; - - uint32 next_socket_id_; - - TCPSocketMap tcp_sockets_; - - NetworkMonitorIdSet network_monitor_ids_; - - base::FilePath browser_path_; - bool incognito_; DISALLOW_COPY_AND_ASSIGN(PepperMessageFilter); }; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc new file mode 100644 index 00000000000..b53a60b779b --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc @@ -0,0 +1,121 @@ +// Copyright 2013 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/pepper_network_monitor_host.h" + +#include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/pepper_socket_utils.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/socket_permission_request.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/private/net_address_private_impl.h" + + +namespace content { + +namespace { + +bool CanUseNetworkMonitor(bool external_plugin, + int render_process_id, + int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + SocketPermissionRequest request = SocketPermissionRequest( + SocketPermissionRequest::NETWORK_STATE, std::string(), 0); + return pepper_socket_utils::CanUseSocketAPIs( + external_plugin, false /* private_api */, &request, render_process_id, + render_view_id); +} + +scoped_ptr<net::NetworkInterfaceList> GetNetworkList() { + scoped_ptr<net::NetworkInterfaceList> list(new net::NetworkInterfaceList()); + net::GetNetworkList(list.get()); + return list.Pass(); +} + +} // namespace + +PepperNetworkMonitorHost::PepperNetworkMonitorHost( + BrowserPpapiHostImpl* host, + PP_Instance instance, + PP_Resource resource) + : ResourceHost(host->GetPpapiHost(), instance, resource), + weak_factory_(this) { + int render_process_id; + int render_view_id; + host->GetRenderViewIDsForInstance(instance, + &render_process_id, + &render_view_id); + + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, FROM_HERE, + base::Bind(&CanUseNetworkMonitor, host->external_plugin(), + render_process_id, render_view_id), + base::Bind(&PepperNetworkMonitorHost::OnPermissionCheckResult, + weak_factory_.GetWeakPtr())); +} + +PepperNetworkMonitorHost::~PepperNetworkMonitorHost() { + net::NetworkChangeNotifier::RemoveIPAddressObserver(this); +} + +void PepperNetworkMonitorHost::OnIPAddressChanged() { + GetAndSendNetworkList(); +} + +void PepperNetworkMonitorHost::OnPermissionCheckResult( + bool can_use_network_monitor) { + if (!can_use_network_monitor) { + host()->SendUnsolicitedReply(pp_resource(), + PpapiPluginMsg_NetworkMonitor_Forbidden()); + return; + } + + net::NetworkChangeNotifier::AddIPAddressObserver(this); + GetAndSendNetworkList(); +} + +void PepperNetworkMonitorHost::GetAndSendNetworkList() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Call GetNetworkList() on a thread that allows blocking IO. + base::PostTaskAndReplyWithResult( + BrowserThread::GetBlockingPool(), FROM_HERE, + base::Bind(&GetNetworkList), + base::Bind(&PepperNetworkMonitorHost::SendNetworkList, + weak_factory_.GetWeakPtr())); +} + +void PepperNetworkMonitorHost::SendNetworkList( + scoped_ptr<net::NetworkInterfaceList> list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + scoped_ptr<ppapi::proxy::SerializedNetworkList> list_copy( + new ppapi::proxy::SerializedNetworkList(list->size())); + for (size_t i = 0; i < list->size(); ++i) { + const net::NetworkInterface& network = list->at(i); + ppapi::proxy::SerializedNetworkInfo& network_copy = list_copy->at(i); + network_copy.name = network.name; + + network_copy.addresses.resize( + 1, ppapi::NetAddressPrivateImpl::kInvalidNetAddress); + bool result = ppapi::NetAddressPrivateImpl::IPEndPointToNetAddress( + network.address, 0, &(network_copy.addresses[0])); + DCHECK(result); + + // TODO(sergeyu): Currently net::NetworkInterfaceList provides + // only name and one IP address. Add all other fields and copy + // them here. + network_copy.type = PP_NETWORKLIST_TYPE_UNKNOWN; + network_copy.state = PP_NETWORKLIST_STATE_UP; + network_copy.display_name = network.name; + network_copy.mtu = 0; + } + host()->SendUnsolicitedReply( + pp_resource(), PpapiPluginMsg_NetworkMonitor_NetworkList(*list_copy)); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h new file mode 100644 index 00000000000..e27f8a2ac21 --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h @@ -0,0 +1,50 @@ +// Copyright 2013 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_PEPPER_NETWORK_MONITOR_HOST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_NETWORK_MONITOR_HOST_H_ + +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "content/common/content_export.h" +#include "net/base/net_util.h" +#include "net/base/network_change_notifier.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/resource_host.h" + +namespace content { + +class BrowserPpapiHostImpl; + +// The host for PPB_NetworkMonitor. This class lives on the IO thread. +class CONTENT_EXPORT PepperNetworkMonitorHost + : public ppapi::host::ResourceHost, + public net::NetworkChangeNotifier::IPAddressObserver { + public: + PepperNetworkMonitorHost( + BrowserPpapiHostImpl* host, + PP_Instance instance, + PP_Resource resource); + + virtual ~PepperNetworkMonitorHost(); + + // net::NetworkChangeNotifier::IPAddressObserver interface. + virtual void OnIPAddressChanged() OVERRIDE; + + private: + void OnPermissionCheckResult(bool can_use_network_monitor); + + void GetAndSendNetworkList(); + void SendNetworkList(scoped_ptr<net::NetworkInterfaceList> list); + + ppapi::host::ReplyMessageContext reply_context_; + + base::WeakPtrFactory<PepperNetworkMonitorHost> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperNetworkMonitorHost); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_NETWORK_MONITOR_HOST_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc index 6042badfc92..13f0fc3733f 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc @@ -84,7 +84,7 @@ PepperNetworkProxyHost::GetUIThreadDataOnUIThread(int render_process_id, result.is_allowed = pepper_socket_utils::CanUseSocketAPIs( is_external_plugin, false /* is_private_api */, - request, + &request, render_view_host); } return result; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc index a5b95ceb223..e4c8aa71dc1 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc @@ -16,6 +16,7 @@ #include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_message_utils.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/ppapi_message_utils.h" #include "ppapi/proxy/resource_message_params.h" namespace content { @@ -28,8 +29,7 @@ PepperRendererConnection::PepperRendererConnection(int render_process_id) "", base::FilePath(), base::FilePath(), - false, - NULL)); + false)); } PepperRendererConnection::~PepperRendererConnection() { @@ -72,10 +72,8 @@ bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg, bool handled = true; IPC_BEGIN_MESSAGE_MAP_EX(PepperRendererConnection, msg, *message_was_ok) - IPC_MESSAGE_HANDLER(PpapiHostMsg_CreateResourceHostFromHost, - OnMsgCreateResourceHostFromHost) - IPC_MESSAGE_HANDLER(PpapiHostMsg_FileRef_GetInfoForRenderer, - OnMsgFileRefGetInfoForRenderer) + IPC_MESSAGE_HANDLER(PpapiHostMsg_CreateResourceHostsFromHost, + OnMsgCreateResourceHostsFromHost) IPC_MESSAGE_HANDLER(ViewHostMsg_DidCreateInProcessInstance, OnMsgDidCreateInProcessInstance) IPC_MESSAGE_HANDLER(ViewHostMsg_DidDeleteInProcessInstance, @@ -86,79 +84,48 @@ bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg, return handled; } -void PepperRendererConnection::OnMsgCreateResourceHostFromHost( +void PepperRendererConnection::OnMsgCreateResourceHostsFromHost( int routing_id, int child_process_id, const ppapi::proxy::ResourceMessageCallParams& params, PP_Instance instance, - const IPC::Message& nested_msg) { + const std::vector<IPC::Message>& nested_msgs) { BrowserPpapiHostImpl* host = GetHostForChildProcess(child_process_id); - int pending_resource_host_id; + std::vector<int> pending_resource_host_ids(nested_msgs.size(), 0); if (!host) { DLOG(ERROR) << "Invalid plugin process ID."; - pending_resource_host_id = 0; } else { - // FileRef_CreateExternal is only permitted from the renderer. Because of - // this, we handle this message here and not in - // content_browser_pepper_host_factory.cc. - scoped_ptr<ppapi::host::ResourceHost> resource_host; - if (host->IsValidInstance(instance)) { - if (nested_msg.type() == PpapiHostMsg_FileRef_CreateExternal::ID) { - base::FilePath external_path; - if (ppapi::UnpackMessage<PpapiHostMsg_FileRef_CreateExternal>( - nested_msg, &external_path)) { - resource_host.reset(new PepperFileRefHost( - host, instance, params.pp_resource(), external_path)); + for (size_t i = 0; i < nested_msgs.size(); ++i) { + // FileRef_CreateExternal is only permitted from the renderer. Because of + // this, we handle this message here and not in + // content_browser_pepper_host_factory.cc. + scoped_ptr<ppapi::host::ResourceHost> resource_host; + if (host->IsValidInstance(instance)) { + if (nested_msgs[i].type() == PpapiHostMsg_FileRef_CreateExternal::ID) { + base::FilePath external_path; + if (ppapi::UnpackMessage<PpapiHostMsg_FileRef_CreateExternal>( + nested_msgs[i], &external_path)) { + resource_host.reset(new PepperFileRefHost( + host, instance, params.pp_resource(), external_path)); + } } } - } - - if (!resource_host.get()) { - resource_host = host->GetPpapiHost()->CreateResourceHost(params, - instance, - nested_msg); - } - pending_resource_host_id = - host->GetPpapiHost()->AddPendingResourceHost(resource_host.Pass()); - } - - Send(new PpapiHostMsg_CreateResourceHostFromHostReply( - routing_id, params.sequence(), pending_resource_host_id)); -} -void PepperRendererConnection::OnMsgFileRefGetInfoForRenderer( - int routing_id, - int child_process_id, - int32_t sequence, - const std::vector<PP_Resource>& resources) { - std::vector<PP_Resource> out_resources; - std::vector<PP_FileSystemType> fs_types; - std::vector<std::string> file_system_url_specs; - std::vector<base::FilePath> external_paths; + if (!resource_host.get()) { + resource_host = host->GetPpapiHost()->CreateResourceHost( + params, instance, nested_msgs[i]); + } - BrowserPpapiHostImpl* host = GetHostForChildProcess(child_process_id); - if (host) { - for (size_t i = 0; i < resources.size(); ++i) { - ppapi::host::ResourceHost* resource_host = - host->GetPpapiHost()->GetResourceHost(resources[i]); - if (resource_host && resource_host->IsFileRefHost()) { - PepperFileRefHost* file_ref_host = - static_cast<PepperFileRefHost*>(resource_host); - out_resources.push_back(resources[i]); - fs_types.push_back(file_ref_host->GetFileSystemType()); - file_system_url_specs.push_back(file_ref_host->GetFileSystemURLSpec()); - external_paths.push_back(file_ref_host->GetExternalPath()); + if (resource_host.get()) { + pending_resource_host_ids[i] = + host->GetPpapiHost()->AddPendingResourceHost(resource_host.Pass()); } } } - Send(new PpapiHostMsg_FileRef_GetInfoForRendererReply( - routing_id, - sequence, - out_resources, - fs_types, - file_system_url_specs, - external_paths)); + + Send(new PpapiHostMsg_CreateResourceHostsFromHostReply( + routing_id, params.sequence(), pending_resource_host_ids)); } void PepperRendererConnection::OnMsgDidCreateInProcessInstance( diff --git a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h index 43923465d0b..157827a9cfd 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_RENDERER_CONNECTION_H_ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_RENDERER_CONNECTION_H_ +#include <vector> + #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" @@ -45,18 +47,12 @@ class PepperRendererConnection : public BrowserMessageFilter { // PepperRendererConnection, which serves as the host for in-process plugins. BrowserPpapiHostImpl* GetHostForChildProcess(int child_process_id) const; - void OnMsgCreateResourceHostFromHost( + void OnMsgCreateResourceHostsFromHost( int routing_id, int child_process_id, const ppapi::proxy::ResourceMessageCallParams& params, PP_Instance instance, - const IPC::Message& nested_msg); - - void OnMsgFileRefGetInfoForRenderer( - int routing_id, - int child_process_id, - int32_t sequence_num, - const std::vector<PP_Resource>& resources); + const std::vector<IPC::Message>& nested_msgs); void OnMsgDidCreateInProcessInstance( PP_Instance instance, diff --git a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc index 5402823f01e..5816ce4831f 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc @@ -10,8 +10,18 @@ namespace content { -bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, - const base::FilePath& file) { +namespace { + +template <typename CanRead, typename CanWrite, + typename CanCreate, typename CanCreateWrite, + typename FileID> +bool CanOpenFileWithPepperFlags(CanRead can_read, + CanWrite can_write, + CanCreate can_create, + CanCreateWrite can_create_write, + int pp_open_flags, + int child_id, + const FileID& file) { ChildProcessSecurityPolicyImpl* policy = ChildProcessSecurityPolicyImpl::GetInstance(); @@ -22,33 +32,53 @@ bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, bool pp_exclusive = !!(pp_open_flags & PP_FILEOPENFLAG_EXCLUSIVE); bool pp_append = !!(pp_open_flags & PP_FILEOPENFLAG_APPEND); - if (pp_read && !policy->CanReadFile(child_id, file)) + if (pp_read && !(policy->*can_read)(child_id, file)) return false; - if (pp_write && !policy->CanWriteFile(child_id, file)) + if (pp_write && !(policy->*can_write)(child_id, file)) return false; - if (pp_append) { - // Given ChildSecurityPolicyImpl's current definition of permissions, - // APPEND is never supported. + // TODO(tommycli): Maybe tighten up required permission. crbug.com/284792 + if (pp_append && !(policy->*can_create_write)(child_id, file)) return false; - } if (pp_truncate && !pp_write) return false; if (pp_create) { if (pp_exclusive) { - return policy->CanCreateFile(child_id, file); + return (policy->*can_create)(child_id, file); } else { // Asks for too much, but this is the only grant that allows overwrite. - return policy->CanCreateWriteFile(child_id, file); + return (policy->*can_create_write)(child_id, file); } } else if (pp_truncate) { - return policy->CanCreateWriteFile(child_id, file); + return (policy->*can_create_write)(child_id, file); } return true; } +} + +bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, + const base::FilePath& file) { + return CanOpenFileWithPepperFlags( + &ChildProcessSecurityPolicyImpl::CanReadFile, + &ChildProcessSecurityPolicyImpl::CanWriteFile, + &ChildProcessSecurityPolicyImpl::CanCreateFile, + &ChildProcessSecurityPolicyImpl::CanCreateWriteFile, + pp_open_flags, child_id, file); +} + +bool CanOpenFileSystemURLWithPepperFlags(int pp_open_flags, int child_id, + const fileapi::FileSystemURL& url) { + return CanOpenFileWithPepperFlags( + &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, + &ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile, + &ChildProcessSecurityPolicyImpl::CanCreateFileSystemFile, + &ChildProcessSecurityPolicyImpl::CanCreateWriteFileSystemFile, + pp_open_flags, child_id, url); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h index 4a3cea5874a..d0a39831324 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h @@ -7,15 +7,23 @@ #include "base/files/file_path.h" #include "content/common/content_export.h" +#include "webkit/browser/fileapi/file_system_url.h" namespace content { -// Helper method that returns whether or not the child process is allowed to +// Helper function that returns whether or not the child process is allowed to // open the specified |file| with the specified |pp_open_flags|. CONTENT_EXPORT bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, const base::FilePath& file); +// Helper function that returns whether or not the child process is allowed to +// open the specified file system |url| with the specified |pp_open_flags|. +CONTENT_EXPORT bool CanOpenFileSystemURLWithPepperFlags( + int pp_open_flags, + int child_id, + const fileapi::FileSystemURL& url); + } // namespace content #endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_SECURITY_HELPER_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc index 9dc585e3315..2f109cddb71 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc @@ -8,13 +8,16 @@ #include <vector> #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" #include "content/public/common/content_client.h" +#include "net/cert/x509_certificate.h" #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/shared_impl/private/net_address_private_impl.h" +#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h" namespace content { namespace pepper_socket_utils { @@ -34,7 +37,7 @@ SocketPermissionRequest CreateSocketPermissionRequest( bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, int render_process_id, int render_view_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -48,7 +51,7 @@ bool CanUseSocketAPIs(bool external_plugin, bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, RenderViewHost* render_view_host) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -77,5 +80,60 @@ bool CanUseSocketAPIs(bool external_plugin, return true; } +bool GetCertificateFields(const net::X509Certificate& cert, + ppapi::PPB_X509Certificate_Fields* fields) { + const net::CertPrincipal& issuer = cert.issuer(); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COMMON_NAME, + new base::StringValue(issuer.common_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_LOCALITY_NAME, + new base::StringValue(issuer.locality_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_STATE_OR_PROVINCE_NAME, + new base::StringValue(issuer.state_or_province_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COUNTRY_NAME, + new base::StringValue(issuer.country_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME, + new base::StringValue(JoinString(issuer.organization_names, '\n'))); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME, + new base::StringValue(JoinString(issuer.organization_unit_names, '\n'))); + + const net::CertPrincipal& subject = cert.subject(); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COMMON_NAME, + new base::StringValue(subject.common_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_LOCALITY_NAME, + new base::StringValue(subject.locality_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_STATE_OR_PROVINCE_NAME, + new base::StringValue(subject.state_or_province_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COUNTRY_NAME, + new base::StringValue(subject.country_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME, + new base::StringValue(JoinString(subject.organization_names, '\n'))); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME, + new base::StringValue(JoinString(subject.organization_unit_names, '\n'))); + + const std::string& serial_number = cert.serial_number(); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SERIAL_NUMBER, + base::BinaryValue::CreateWithCopiedBuffer(serial_number.data(), + serial_number.length())); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_BEFORE, + new base::FundamentalValue(cert.valid_start().ToDoubleT())); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_AFTER, + new base::FundamentalValue(cert.valid_expiry().ToDoubleT())); + std::string der; + net::X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_RAW, + base::BinaryValue::CreateWithCopiedBuffer(der.data(), der.length())); + return true; +} + +bool GetCertificateFields(const char* der, + uint32_t length, + ppapi::PPB_X509Certificate_Fields* fields) { + scoped_refptr<net::X509Certificate> cert = + net::X509Certificate::CreateFromBytes(der, length); + if (!cert.get()) + return false; + return GetCertificateFields(*cert.get(), fields); +} + } // namespace pepper_socket_utils } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h index 7a0cef56a5c..1a1a26cac19 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h @@ -6,9 +6,18 @@ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_SOCKET_UTILS_H_ #include "content/public/common/socket_permission_request.h" +#include "ppapi/c/pp_stdint.h" struct PP_NetAddress_Private; +namespace net { +class X509Certificate; +} + +namespace ppapi { +class PPB_X509Certificate_Fields; +} + namespace content { class RenderViewHost; @@ -19,18 +28,32 @@ SocketPermissionRequest CreateSocketPermissionRequest( SocketPermissionRequest::OperationType type, const PP_NetAddress_Private& net_addr); +// Returns true if the socket operation specified by |params| is allowed. +// If |params| is NULL, this method checks the basic "socket" permission, which +// is for those operations that don't require a specific socket permission rule. bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, int render_process_id, int render_view_id); // TODO (ygorshenin@): remove this method. bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, RenderViewHost* render_view_host); +// Extracts the certificate field data from a net::X509Certificate into +// PPB_X509Certificate_Fields. +bool GetCertificateFields(const net::X509Certificate& cert, + ppapi::PPB_X509Certificate_Fields* fields); + +// Extracts the certificate field data from the DER representation of a +// certificate into PPB_X509Certificate_Fields. +bool GetCertificateFields(const char* der, + uint32_t length, + ppapi::PPB_X509Certificate_Fields* fields); + } // namespace pepper_socket_utils } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc index 48c8291cab9..063c11ed784 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc @@ -8,20 +8,22 @@ #include "base/bind_helpers.h" #include "base/logging.h" #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" #include "content/browser/renderer_host/pepper/pepper_socket_utils.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/socket_permission_request.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" -#include "net/socket/tcp_client_socket.h" -#include "net/socket/tcp_server_socket.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/error_conversion.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/api_id.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" #include "ppapi/shared_impl/private/net_address_private_impl.h" using ppapi::NetAddressPrivateImpl; @@ -36,18 +38,21 @@ size_t g_num_instances = 0; namespace content { PepperTCPServerSocketMessageFilter::PepperTCPServerSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter) - : state_(STATE_BEFORE_LISTENING), - pepper_message_filter_(pepper_message_filter), + bool private_api) + : ppapi_host_(host->GetPpapiHost()), + factory_(factory), + instance_(instance), + state_(STATE_BEFORE_LISTENING), external_plugin_(host->external_plugin()), private_api_(private_api), render_process_id_(0), render_view_id_(0) { ++g_num_instances; - DCHECK(host); + DCHECK(factory_); + DCHECK(ppapi_host_); if (!host->GetRenderViewIDsForInstance(instance, &render_process_id_, &render_view_id_)) { @@ -83,7 +88,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnResourceMessageReceived( IPC_BEGIN_MESSAGE_MAP(PepperTCPServerSocketMessageFilter, msg) PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_TCPServerSocket_Listen, OnMsgListen) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( PpapiHostMsg_TCPServerSocket_Accept, OnMsgAccept) PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( PpapiHostMsg_TCPServerSocket_StopListening, OnMsgStopListening) @@ -103,7 +108,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgListen( content::SocketPermissionRequest::TCP_LISTEN, addr); if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, + &request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; @@ -117,8 +122,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgListen( } int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept( - const ppapi::host::HostMessageContext* context, - uint32 plugin_dispatcher_id) { + const ppapi::host::HostMessageContext* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(context); @@ -129,12 +133,12 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept( ppapi::host::ReplyMessageContext reply_context( context->MakeReplyMessageContext()); int net_result = socket_->Accept( - &socket_buffer_, + &accepted_socket_, + &accepted_address_, base::Bind(&PepperTCPServerSocketMessageFilter::OnAcceptCompleted, - base::Unretained(this), - reply_context, plugin_dispatcher_id)); + base::Unretained(this), reply_context)); if (net_result != net::ERR_IO_PENDING) - OnAcceptCompleted(reply_context, plugin_dispatcher_id, net_result); + OnAcceptCompleted(reply_context, net_result); return PP_OK_COMPLETIONPENDING; } @@ -165,8 +169,22 @@ void PepperTCPServerSocketMessageFilter::DoListen( state_ = STATE_LISTEN_IN_PROGRESS; - socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source())); - int net_result = socket_->Listen(net::IPEndPoint(address, port), backlog); + socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source())); + int net_result = net::OK; + do { + net::IPEndPoint ip_end_point(address, port); + net_result = socket_->Open(ip_end_point.GetFamily()); + if (net_result != net::OK) + break; + net_result = socket_->SetDefaultOptionsForServer(); + if (net_result != net::OK) + break; + net_result = socket_->Bind(ip_end_point); + if (net_result != net::OK) + break; + net_result = socket_->Listen(backlog); + } while (false); + if (net_result != net::ERR_IO_PENDING) OnListenCompleted(context, net_result); } @@ -211,7 +229,6 @@ void PepperTCPServerSocketMessageFilter::OnListenCompleted( void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( const ppapi::host::ReplyMessageContext& context, - uint32 plugin_dispatcher_id, int net_result) { if (state_ != STATE_ACCEPT_IN_PROGRESS) { SendAcceptError(context, PP_ERROR_FAILED); @@ -226,16 +243,15 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( return; } - DCHECK(socket_buffer_.get()); + DCHECK(accepted_socket_.get()); - scoped_ptr<net::StreamSocket> socket(socket_buffer_.release()); net::IPEndPoint ip_end_point_local; - net::IPEndPoint ip_end_point_remote; PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress; PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress; int32_t pp_result = - NetErrorToPepperError(socket->GetLocalAddress(&ip_end_point_local)); + NetErrorToPepperError(accepted_socket_->GetLocalAddress( + &ip_end_point_local)); if (pp_result != PP_OK) { SendAcceptError(context, pp_result); return; @@ -243,33 +259,27 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( if (!NetAddressPrivateImpl::IPEndPointToNetAddress( ip_end_point_local.address(), ip_end_point_local.port(), - &local_addr)) { - SendAcceptError(context, PP_ERROR_FAILED); - return; - } - pp_result = - NetErrorToPepperError(socket->GetPeerAddress(&ip_end_point_remote)); - if (pp_result != PP_OK) { - SendAcceptError(context, pp_result); - return; - } - if (!NetAddressPrivateImpl::IPEndPointToNetAddress( - ip_end_point_remote.address(), - ip_end_point_remote.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + accepted_address_.address(), + accepted_address_.port(), &remote_addr)) { SendAcceptError(context, PP_ERROR_FAILED); return; } - if (!pepper_message_filter_.get() || plugin_dispatcher_id == 0) { - SendAcceptError(context, PP_ERROR_FAILED); + + scoped_ptr<ppapi::host::ResourceHost> host = + factory_->CreateAcceptedTCPSocket( + instance_, ppapi::TCP_SOCKET_VERSION_PRIVATE, + accepted_socket_.Pass()); + if (!host) { + SendAcceptError(context, PP_ERROR_NOSPACE); return; } - uint32 accepted_socket_id = pepper_message_filter_->AddAcceptedTCPSocket( - ppapi::API_ID_PPB_TCPSOCKET_PRIVATE, - plugin_dispatcher_id, - socket.release()); - if (accepted_socket_id != 0) { - SendAcceptReply(context, PP_OK, accepted_socket_id, local_addr, + int pending_resource_id = ppapi_host_->AddPendingResourceHost(host.Pass()); + if (pending_resource_id) { + SendAcceptReply(context, PP_OK, pending_resource_id, + local_addr, remote_addr); } else { SendAcceptError(context, PP_ERROR_NOSPACE); @@ -296,13 +306,13 @@ void PepperTCPServerSocketMessageFilter::SendListenError( void PepperTCPServerSocketMessageFilter::SendAcceptReply( const ppapi::host::ReplyMessageContext& context, int32_t pp_result, - uint32 accepted_socket_id, + int pending_resource_id, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr) { ppapi::host::ReplyMessageContext reply_context(context); reply_context.params.set_result(pp_result); SendReply(reply_context, PpapiPluginMsg_TCPServerSocket_AcceptReply( - accepted_socket_id, local_addr, remote_addr)); + pending_resource_id, local_addr, remote_addr)); } void PepperTCPServerSocketMessageFilter::SendAcceptError( diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h index ad8cf19f287..5d17333c9e8 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h @@ -9,30 +9,35 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "content/browser/renderer_host/pepper/pepper_message_filter.h" #include "content/common/content_export.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_socket.h" #include "ppapi/c/pp_instance.h" #include "ppapi/host/resource_message_filter.h" struct PP_NetAddress_Private; -namespace net { -class ServerSocket; -class StreamSocket; +namespace ppapi { +namespace host { +class PpapiHost; +} } namespace content { class BrowserPpapiHostImpl; +class ContentBrowserPepperHostFactory; +// TODO(yzshen): Remove this class entirely and let +// TCPServerSocketPrivateResource inherit TCPSocketResourceBase. class CONTENT_EXPORT PepperTCPServerSocketMessageFilter : public ppapi::host::ResourceMessageFilter { public: PepperTCPServerSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter); + bool private_api); static size_t GetNumInstances(); @@ -58,8 +63,7 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter int32_t OnMsgListen(const ppapi::host::HostMessageContext* context, const PP_NetAddress_Private& addr, int32_t backlog); - int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context, - uint32 plugin_dispatcher_id); + int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context); int32_t OnMsgStopListening(const ppapi::host::HostMessageContext* context); void DoListen(const ppapi::host::ReplyMessageContext& context, @@ -69,7 +73,6 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter void OnListenCompleted(const ppapi::host::ReplyMessageContext& context, int net_result); void OnAcceptCompleted(const ppapi::host::ReplyMessageContext& context, - uint32 plugin_dispatcher_id, int net_result); void SendListenReply(const ppapi::host::ReplyMessageContext& context, @@ -79,17 +82,23 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter int32_t pp_result); void SendAcceptReply(const ppapi::host::ReplyMessageContext& context, int32_t pp_result, - uint32 accepted_socket_id, + int pending_resource_id, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr); void SendAcceptError(const ppapi::host::ReplyMessageContext& context, int32_t pp_result); // Following fields are initialized and used only on the IO thread. + // Non-owning ptr. + ppapi::host::PpapiHost* ppapi_host_; + // Non-owning ptr. + ContentBrowserPepperHostFactory* factory_; + PP_Instance instance_; + State state_; - scoped_ptr<net::ServerSocket> socket_; - scoped_ptr<net::StreamSocket> socket_buffer_; - scoped_refptr<PepperMessageFilter> pepper_message_filter_; + scoped_ptr<net::TCPSocket> socket_; + scoped_ptr<net::TCPSocket> accepted_socket_; + net::IPEndPoint accepted_address_; // Following fields are initialized on the IO thread but used only // on the UI thread. diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc index f05be8783ed..c5b752aa7f5 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc @@ -87,10 +87,12 @@ void PepperTCPSocket::Connect(const std::string& host, uint16_t port) { connection_state_ = CONNECT_IN_PROGRESS; net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port)); - resolver_.reset(new net::SingleRequestHostResolver( - manager_->GetHostResolver())); + resolver_.reset( + new net::SingleRequestHostResolver(manager_->GetHostResolver())); int net_result = resolver_->Resolve( - request_info, &address_list_, + request_info, + net::DEFAULT_PRIORITY, + &address_list_, base::Bind(&PepperTCPSocket::OnResolveCompleted, base::Unretained(this)), net::BoundNetLog()); if (net_result != net::ERR_IO_PENDING) @@ -141,16 +143,16 @@ void PepperTCPSocket::SSLHandshake( connection_state_ = SSL_HANDSHAKE_IN_PROGRESS; // TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting. - net::ClientSocketHandle* handle = new net::ClientSocketHandle(); - handle->set_socket(socket_.release()); + scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle()); + handle->SetSocket(socket_.Pass()); net::ClientSocketFactory* factory = net::ClientSocketFactory::GetDefaultFactory(); net::HostPortPair host_port_pair(server_name, server_port); net::SSLClientSocketContext ssl_context; ssl_context.cert_verifier = manager_->GetCertVerifier(); ssl_context.transport_security_state = manager_->GetTransportSecurityState(); - socket_.reset(factory->CreateSSLClientSocket( - handle, host_port_pair, manager_->ssl_config(), ssl_context)); + socket_ = factory->CreateSSLClientSocket( + handle.Pass(), host_port_pair, manager_->ssl_config(), ssl_context); if (!socket_) { LOG(WARNING) << "Failed to create an SSL client socket."; OnSSLHandshakeCompleted(net::ERR_UNEXPECTED); diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.h deleted file mode 100644 index 986afb015bb..00000000000 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2012 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_PEPPER_TCP_SOCKET_H_ -#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/address_list.h" -#include "net/base/completion_callback.h" -#include "ppapi/c/pp_stdint.h" -#include "ppapi/c/ppb_tcp_socket.h" - -struct PP_NetAddress_Private; - -namespace ppapi { -class PPB_X509Certificate_Fields; -class SocketOptionData; -} - -namespace net { -class DrainableIOBuffer; -class IOBuffer; -class SingleRequestHostResolver; -class StreamSocket; -class X509Certificate; -} - -namespace content { -class PepperMessageFilter; - -// PepperTCPSocket is used by PepperMessageFilter to handle requests from -// the Pepper TCP socket API (PPB_TCPSocket and PPB_TCPSocket_Private). -class PepperTCPSocket { - public: - PepperTCPSocket(PepperMessageFilter* manager, - int32 routing_id, - uint32 plugin_dispatcher_id, - uint32 socket_id, - bool private_api); - - // Used for creation already connected sockets. Takes ownership of - // |socket|. - PepperTCPSocket(PepperMessageFilter* manager, - int32 routing_id, - uint32 plugin_dispatcher_id, - uint32 socket_id, - net::StreamSocket* socket, - bool private_api); - ~PepperTCPSocket(); - - int routing_id() { return routing_id_; } - bool private_api() const { return private_api_; } - - void Connect(const std::string& host, uint16_t port); - void ConnectWithNetAddress(const PP_NetAddress_Private& net_addr); - void SSLHandshake( - const std::string& server_name, - uint16_t server_port, - const std::vector<std::vector<char> >& trusted_certs, - const std::vector<std::vector<char> >& untrusted_certs); - void Read(int32 bytes_to_read); - void Write(const std::string& data); - void SetOption(PP_TCPSocket_Option name, - const ppapi::SocketOptionData& value); - - void SendConnectACKError(int32_t error); - - // Extracts the certificate field data from a |net::X509Certificate| into - // |PPB_X509Certificate_Fields|. - static bool GetCertificateFields(const net::X509Certificate& cert, - ppapi::PPB_X509Certificate_Fields* fields); - // Extracts the certificate field data from the DER representation of a - // certificate into |PPB_X509Certificate_Fields|. - static bool GetCertificateFields(const char* der, - uint32_t length, - ppapi::PPB_X509Certificate_Fields* fields); - - private: - enum ConnectionState { - // Before a connection is successfully established (including a previous - // connect request failed). - BEFORE_CONNECT, - // There is a connect request that is pending. - CONNECT_IN_PROGRESS, - // A connection has been successfully established. - CONNECTED, - // There is an SSL handshake request that is pending. - SSL_HANDSHAKE_IN_PROGRESS, - // An SSL connection has been successfully established. - SSL_CONNECTED, - // An SSL handshake has failed. - SSL_HANDSHAKE_FAILED - }; - - void StartConnect(const net::AddressList& addresses); - - void SendReadACKError(int32_t error); - void SendWriteACKError(int32_t error); - void SendSSLHandshakeACK(bool succeeded); - void SendSetOptionACK(int32_t result); - - void OnResolveCompleted(int net_result); - void OnConnectCompleted(int net_result); - void OnSSLHandshakeCompleted(int net_result); - void OnReadCompleted(int net_result); - void OnWriteCompleted(int net_result); - - bool IsConnected() const; - bool IsSsl() const; - - // Actually does a write from |write_buffer_|; possibly called many times for - // each |Write()|. - void DoWrite(); - - PepperMessageFilter* manager_; - int32 routing_id_; - uint32 plugin_dispatcher_id_; - uint32 socket_id_; - bool private_api_; - - ConnectionState connection_state_; - bool end_of_file_reached_; - - scoped_ptr<net::SingleRequestHostResolver> resolver_; - net::AddressList address_list_; - - scoped_ptr<net::StreamSocket> socket_; - - scoped_refptr<net::IOBuffer> read_buffer_; - - // |StreamSocket::Write()| may not always write the full buffer, but we would - // rather have our |Write()| do so whenever possible. To do this, we may have - // to call the former multiple times for each of the latter. This entails - // using a |DrainableIOBuffer|, which requires an underlying base |IOBuffer|. - scoped_refptr<net::IOBuffer> write_buffer_base_; - scoped_refptr<net::DrainableIOBuffer> write_buffer_; - - DISALLOW_COPY_AND_ASSIGN(PepperTCPSocket); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc new file mode 100644 index 00000000000..f943f39eb9e --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc @@ -0,0 +1,979 @@ +// Copyright 2013 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/pepper_tcp_socket_message_filter.h" + +#include <cstring> + +#include "base/bind.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" +#include "content/browser/renderer_host/pepper/pepper_socket_utils.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_context.h" +#include "content/public/common/socket_permission_request.h" +#include "net/base/address_family.h" +#include "net/base/host_port_pair.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/dns/single_request_host_resolver.h" +#include "net/socket/client_socket_factory.h" +#include "net/socket/client_socket_handle.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/tcp_client_socket.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/error_conversion.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/host/resource_host.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/tcp_socket_resource_base.h" +#include "ppapi/shared_impl/private/net_address_private_impl.h" + +using ppapi::NetAddressPrivateImpl; +using ppapi::host::NetErrorToPepperError; +using ppapi::proxy::TCPSocketResourceBase; +using ppapi::TCPSocketState; +using ppapi::TCPSocketVersion; + +namespace { + +size_t g_num_instances = 0; + +} // namespace + +namespace content { + +PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, + BrowserPpapiHostImpl* host, + PP_Instance instance, + TCPSocketVersion version) + : version_(version), + external_plugin_(host->external_plugin()), + render_process_id_(0), + render_view_id_(0), + ppapi_host_(host->GetPpapiHost()), + factory_(factory), + instance_(instance), + state_(TCPSocketState::INITIAL), + end_of_file_reached_(false), + bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress), + address_index_(0), + socket_(new net::TCPSocket(NULL, net::NetLog::Source())), + ssl_context_helper_(host->ssl_context_helper()), + pending_accept_(false) { + DCHECK(host); + ++g_num_instances; + if (!host->GetRenderViewIDsForInstance(instance, + &render_process_id_, + &render_view_id_)) { + NOTREACHED(); + } +} + +PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( + BrowserPpapiHostImpl* host, + PP_Instance instance, + TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket) + : version_(version), + external_plugin_(host->external_plugin()), + render_process_id_(0), + render_view_id_(0), + ppapi_host_(host->GetPpapiHost()), + factory_(NULL), + instance_(instance), + state_(TCPSocketState::CONNECTED), + end_of_file_reached_(false), + bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress), + address_index_(0), + socket_(socket.Pass()), + ssl_context_helper_(host->ssl_context_helper()), + pending_accept_(false) { + DCHECK(host); + DCHECK_NE(version, ppapi::TCP_SOCKET_VERSION_1_0); + + ++g_num_instances; + if (!host->GetRenderViewIDsForInstance(instance, + &render_process_id_, + &render_view_id_)) { + NOTREACHED(); + } +} + +PepperTCPSocketMessageFilter::~PepperTCPSocketMessageFilter() { + if (socket_) + socket_->Close(); + if (ssl_socket_) + ssl_socket_->Disconnect(); + --g_num_instances; +} + +// static +size_t PepperTCPSocketMessageFilter::GetNumInstances() { + return g_num_instances; +} + +scoped_refptr<base::TaskRunner> +PepperTCPSocketMessageFilter::OverrideTaskRunnerForMessage( + const IPC::Message& message) { + switch (message.type()) { + case PpapiHostMsg_TCPSocket_Bind::ID: + case PpapiHostMsg_TCPSocket_Connect::ID: + case PpapiHostMsg_TCPSocket_ConnectWithNetAddress::ID: + case PpapiHostMsg_TCPSocket_Listen::ID: + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); + case PpapiHostMsg_TCPSocket_SSLHandshake::ID: + case PpapiHostMsg_TCPSocket_Read::ID: + case PpapiHostMsg_TCPSocket_Write::ID: + case PpapiHostMsg_TCPSocket_Accept::ID: + case PpapiHostMsg_TCPSocket_Close::ID: + case PpapiHostMsg_TCPSocket_SetOption::ID: + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); + } + return NULL; +} + +int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + IPC_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Bind, OnMsgBind) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Connect, OnMsgConnect) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_ConnectWithNetAddress, + OnMsgConnectWithNetAddress) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_SSLHandshake, OnMsgSSLHandshake) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Read, OnMsgRead) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Write, OnMsgWrite) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Listen, OnMsgListen) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_TCPSocket_Accept, OnMsgAccept) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_TCPSocket_Close, OnMsgClose) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_SetOption, OnMsgSetOption) + IPC_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgBind( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket v1.1 or above. + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, false /* private_api */, NULL, render_process_id_, + render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + bind_input_addr_ = net_addr; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoBind, this, + context->MakeReplyMessageContext(), net_addr)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgConnect( + const ppapi::host::HostMessageContext* context, + const std::string& host, + uint16_t port) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket_Private. + if (!IsPrivateAPI()) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + SocketPermissionRequest request(SocketPermissionRequest::TCP_CONNECT, + host, + port); + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, true /* private_api */, &request, + render_process_id_, render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + RenderProcessHost* render_process_host = + RenderProcessHost::FromID(render_process_id_); + if (!render_process_host) + return PP_ERROR_FAILED; + BrowserContext* browser_context = render_process_host->GetBrowserContext(); + if (!browser_context || !browser_context->GetResourceContext()) + return PP_ERROR_FAILED; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoConnect, this, + context->MakeReplyMessageContext(), + host, port, browser_context->GetResourceContext())); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgConnectWithNetAddress( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + content::SocketPermissionRequest request = + pepper_socket_utils::CreateSocketPermissionRequest( + content::SocketPermissionRequest::TCP_CONNECT, net_addr); + if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, IsPrivateAPI(), + &request, render_process_id_, + render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoConnectWithNetAddress, this, + context->MakeReplyMessageContext(), net_addr)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake( + const ppapi::host::HostMessageContext* context, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Allow to do SSL handshake only if currently the socket has been connected + // and there isn't pending read or write. + if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT) || + read_buffer_.get() || write_buffer_base_.get() || write_buffer_.get()) { + return PP_ERROR_FAILED; + } + + // TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting. + net::IPEndPoint peer_address; + if (socket_->GetPeerAddress(&peer_address) != net::OK) + return PP_ERROR_FAILED; + + scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle()); + handle->SetSocket(make_scoped_ptr<net::StreamSocket>( + new net::TCPClientSocket(socket_.Pass(), peer_address))); + net::ClientSocketFactory* factory = + net::ClientSocketFactory::GetDefaultFactory(); + net::HostPortPair host_port_pair(server_name, server_port); + net::SSLClientSocketContext ssl_context; + ssl_context.cert_verifier = ssl_context_helper_->GetCertVerifier(); + ssl_context.transport_security_state = + ssl_context_helper_->GetTransportSecurityState(); + ssl_socket_ = factory->CreateSSLClientSocket( + handle.Pass(), host_port_pair, ssl_context_helper_->ssl_config(), + ssl_context); + if (!ssl_socket_) { + LOG(WARNING) << "Failed to create an SSL client socket."; + state_.CompletePendingTransition(false); + return PP_ERROR_FAILED; + } + + state_.SetPendingTransition(TCPSocketState::SSL_CONNECT); + + const ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + int net_result = ssl_socket_->Connect( + base::Bind(&PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted, + base::Unretained(this), reply_context)); + if (net_result != net::ERR_IO_PENDING) + OnSSLHandshakeCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgRead( + const ppapi::host::HostMessageContext* context, + int32_t bytes_to_read) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!state_.IsConnected() || end_of_file_reached_) + return PP_ERROR_FAILED; + if (read_buffer_.get()) + return PP_ERROR_INPROGRESS; + if (bytes_to_read <= 0 || + bytes_to_read > TCPSocketResourceBase::kMaxReadSize) { + return PP_ERROR_BADARGUMENT; + } + + ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + read_buffer_ = new net::IOBuffer(bytes_to_read); + + int net_result = net::ERR_FAILED; + if (socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED); + net_result = socket_->Read( + read_buffer_.get(), + bytes_to_read, + base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, + base::Unretained(this), reply_context)); + } else if (ssl_socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED); + net_result = ssl_socket_->Read( + read_buffer_.get(), + bytes_to_read, + base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, + base::Unretained(this), reply_context)); + } + if (net_result != net::ERR_IO_PENDING) + OnReadCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgWrite( + const ppapi::host::HostMessageContext* context, + const std::string& data) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsConnected()) + return PP_ERROR_FAILED; + if (write_buffer_base_.get() || write_buffer_.get()) + return PP_ERROR_INPROGRESS; + + size_t data_size = data.size(); + if (data_size == 0 || + data_size > static_cast<size_t>(TCPSocketResourceBase::kMaxWriteSize)) { + return PP_ERROR_BADARGUMENT; + } + + write_buffer_base_ = new net::IOBuffer(data_size); + memcpy(write_buffer_base_->data(), data.data(), data_size); + write_buffer_ = + new net::DrainableIOBuffer(write_buffer_base_.get(), data_size); + DoWrite(context->MakeReplyMessageContext()); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgListen( + const ppapi::host::HostMessageContext* context, + int32_t backlog) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket v1.1 or above. + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + content::SocketPermissionRequest request = + pepper_socket_utils::CreateSocketPermissionRequest( + content::SocketPermissionRequest::TCP_LISTEN, bind_input_addr_); + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, false /* private_api */, &request, + render_process_id_, render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoListen, this, + context->MakeReplyMessageContext(), backlog)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgAccept( + const ppapi::host::HostMessageContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (pending_accept_) + return PP_ERROR_INPROGRESS; + if (state_.state() != TCPSocketState::LISTENING) + return PP_ERROR_FAILED; + + pending_accept_ = true; + ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + int net_result = socket_->Accept( + &accepted_socket_, + &accepted_address_, + base::Bind(&PepperTCPSocketMessageFilter::OnAcceptCompleted, + base::Unretained(this), reply_context)); + if (net_result != net::ERR_IO_PENDING) + OnAcceptCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgClose( + const ppapi::host::HostMessageContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (state_.state() == TCPSocketState::CLOSED) + return PP_OK; + + state_.DoTransition(TCPSocketState::CLOSE, true); + // Make sure we get no further callbacks from |socket_| or |ssl_socket_|. + if (socket_) { + socket_->Close(); + } else if (ssl_socket_) { + ssl_socket_->Disconnect(); + } + return PP_OK; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgSetOption( + const ppapi::host::HostMessageContext* context, + PP_TCPSocket_Option name, + const ppapi::SocketOptionData& value) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + switch (name) { + case PP_TCPSOCKET_OPTION_NO_DELAY: { + if (state_.state() != TCPSocketState::CONNECTED) + return PP_ERROR_FAILED; + + bool boolean_value = false; + if (!value.GetBool(&boolean_value)) + return PP_ERROR_BADARGUMENT; + return socket_->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED; + } + case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE: + case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: { + if (state_.state() != TCPSocketState::CONNECTED) + return PP_ERROR_FAILED; + + int32_t integer_value = 0; + if (!value.GetInt32(&integer_value) || integer_value <= 0) + return PP_ERROR_BADARGUMENT; + + bool result = false; + if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) { + if (integer_value > TCPSocketResourceBase::kMaxSendBufferSize) + return PP_ERROR_BADARGUMENT; + result = socket_->SetSendBufferSize(integer_value); + } else { + if (integer_value > TCPSocketResourceBase::kMaxReceiveBufferSize) + return PP_ERROR_BADARGUMENT; + result = socket_->SetReceiveBufferSize(integer_value); + } + return result ? PP_OK : PP_ERROR_FAILED; + } + default: { + NOTREACHED(); + return PP_ERROR_BADARGUMENT; + } + } +} + +void PepperTCPSocketMessageFilter::DoBind( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (state_.IsPending(TCPSocketState::BIND)) { + SendBindError(context, PP_ERROR_INPROGRESS); + return; + } + if (!state_.IsValidTransition(TCPSocketState::BIND)) { + SendBindError(context, PP_ERROR_FAILED); + return; + } + + int pp_result = PP_OK; + do { + net::IPAddressNumber address; + int port; + if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address, + &port)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + net::IPEndPoint bind_addr(address, port); + + DCHECK(!socket_->IsValid()); + pp_result = NetErrorToPepperError(socket_->Open(bind_addr.GetFamily())); + if (pp_result != PP_OK) + break; + + pp_result = NetErrorToPepperError(socket_->SetDefaultOptionsForServer()); + if (pp_result != PP_OK) + break; + + pp_result = NetErrorToPepperError(socket_->Bind(bind_addr)); + if (pp_result != PP_OK) + break; + + net::IPEndPoint ip_end_point_local; + pp_result = NetErrorToPepperError( + socket_->GetLocalAddress(&ip_end_point_local)); + if (pp_result != PP_OK) + break; + + PP_NetAddress_Private local_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + + SendBindReply(context, PP_OK, local_addr); + state_.DoTransition(TCPSocketState::BIND, true); + return; + } while (false); + if (socket_->IsValid()) + socket_->Close(); + SendBindError(context, pp_result); + state_.DoTransition(TCPSocketState::BIND, false); +} + +void PepperTCPSocketMessageFilter::DoConnect( + const ppapi::host::ReplyMessageContext& context, + const std::string& host, + uint16_t port, + ResourceContext* resource_context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) { + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + state_.SetPendingTransition(TCPSocketState::CONNECT); + address_index_ = 0; + address_list_.clear(); + net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port)); + resolver_.reset(new net::SingleRequestHostResolver( + resource_context->GetHostResolver())); + int net_result = resolver_->Resolve( + request_info, + net::DEFAULT_PRIORITY, + &address_list_, + base::Bind(&PepperTCPSocketMessageFilter::OnResolveCompleted, + base::Unretained(this), context), + net::BoundNetLog()); + if (net_result != net::ERR_IO_PENDING) + OnResolveCompleted(context, net_result); +} + +void PepperTCPSocketMessageFilter::DoConnectWithNetAddress( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) { + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + state_.SetPendingTransition(TCPSocketState::CONNECT); + + net::IPAddressNumber address; + int port; + if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address, + &port)) { + state_.CompletePendingTransition(false); + SendConnectError(context, PP_ERROR_ADDRESS_INVALID); + return; + } + + // Copy the single IPEndPoint to address_list_. + address_index_ = 0; + address_list_.clear(); + address_list_.push_back(net::IPEndPoint(address, port)); + StartConnect(context); +} + +void PepperTCPSocketMessageFilter::DoWrite( + const ppapi::host::ReplyMessageContext& context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(write_buffer_base_.get()); + DCHECK(write_buffer_.get()); + DCHECK_GT(write_buffer_->BytesRemaining(), 0); + DCHECK(state_.IsConnected()); + + int net_result = net::ERR_FAILED; + if (socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED); + net_result = socket_->Write( + write_buffer_.get(), + write_buffer_->BytesRemaining(), + base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, + base::Unretained(this), context)); + } else if (ssl_socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED); + net_result = ssl_socket_->Write( + write_buffer_.get(), + write_buffer_->BytesRemaining(), + base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, + base::Unretained(this), context)); + } + if (net_result != net::ERR_IO_PENDING) + OnWriteCompleted(context, net_result); +} + +void PepperTCPSocketMessageFilter::DoListen( + const ppapi::host::ReplyMessageContext& context, + int32_t backlog) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (state_.IsPending(TCPSocketState::LISTEN)) { + SendListenReply(context, PP_ERROR_INPROGRESS); + return; + } + if (!state_.IsValidTransition(TCPSocketState::LISTEN)) { + SendListenReply(context, PP_ERROR_FAILED); + return; + } + + int32_t pp_result = NetErrorToPepperError(socket_->Listen(backlog)); + SendListenReply(context, pp_result); + state_.DoTransition(TCPSocketState::LISTEN, pp_result == PP_OK); +} + +void PepperTCPSocketMessageFilter::OnResolveCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsPending(TCPSocketState::CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + if (net_result != net::OK) { + SendConnectError(context, NetErrorToPepperError(net_result)); + state_.CompletePendingTransition(false); + return; + } + + StartConnect(context); +} + +void PepperTCPSocketMessageFilter::StartConnect( + const ppapi::host::ReplyMessageContext& context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(state_.IsPending(TCPSocketState::CONNECT)); + DCHECK_LT(address_index_, address_list_.size()); + + int net_result = net::OK; + if (!socket_->IsValid()) + net_result = socket_->Open(address_list_[address_index_].GetFamily()); + + if (net_result == net::OK) { + net_result = socket_->Connect( + address_list_[address_index_], + base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted, + base::Unretained(this), context)); + } + if (net_result != net::ERR_IO_PENDING) + OnConnectCompleted(context, net_result); +} + +void PepperTCPSocketMessageFilter::OnConnectCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsPending(TCPSocketState::CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + int32_t pp_result = NetErrorToPepperError(net_result); + do { + if (pp_result != PP_OK) + break; + + net::IPEndPoint ip_end_point_local; + net::IPEndPoint ip_end_point_remote; + pp_result = NetErrorToPepperError( + socket_->GetLocalAddress(&ip_end_point_local)); + if (pp_result != PP_OK) + break; + pp_result = NetErrorToPepperError( + socket_->GetPeerAddress(&ip_end_point_remote)); + if (pp_result != PP_OK) + break; + + PP_NetAddress_Private local_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + PP_NetAddress_Private remote_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_remote.address(), + ip_end_point_remote.port(), + &remote_addr)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + + socket_->SetDefaultOptionsForClient(); + SendConnectReply(context, PP_OK, local_addr, remote_addr); + state_.CompletePendingTransition(true); + return; + } while (false); + + if (version_ == ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + DCHECK_EQ(1u, address_list_.size()); + + SendConnectError(context, pp_result); + state_.CompletePendingTransition(false); + } else { + // We have to recreate |socket_| because it doesn't allow a second connect + // attempt. We won't lose any state such as bound address or set options, + // because in the private or v1.0 API, connect must be the first operation. + socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source())); + + if (address_index_ + 1 < address_list_.size()) { + DCHECK_EQ(version_, ppapi::TCP_SOCKET_VERSION_PRIVATE); + address_index_++; + StartConnect(context); + } else { + SendConnectError(context, pp_result); + // In order to maintain backward compatibility, allow further attempts to + // connect the socket. + state_ = TCPSocketState(TCPSocketState::INITIAL); + } + } +} + +void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsPending(TCPSocketState::SSL_CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); + SendSSLHandshakeReply(context, PP_ERROR_FAILED); + return; + } + + SendSSLHandshakeReply(context, NetErrorToPepperError(net_result)); + state_.CompletePendingTransition(net_result == net::OK); +} + +void PepperTCPSocketMessageFilter::OnReadCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(read_buffer_.get()); + + if (net_result > 0) { + SendReadReply(context, + PP_OK, + std::string(read_buffer_->data(), net_result)); + } else if (net_result == 0) { + end_of_file_reached_ = true; + SendReadReply(context, PP_OK, std::string()); + } else { + SendReadError(context, NetErrorToPepperError(net_result)); + } + read_buffer_ = NULL; +} + +void PepperTCPSocketMessageFilter::OnWriteCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(write_buffer_base_.get()); + DCHECK(write_buffer_.get()); + + // Note: For partial writes of 0 bytes, don't continue writing to avoid a + // likely infinite loop. + if (net_result > 0) { + write_buffer_->DidConsume(net_result); + if (write_buffer_->BytesRemaining() > 0 && state_.IsConnected()) { + DoWrite(context); + return; + } + } + + if (net_result >= 0) + SendWriteReply(context, write_buffer_->BytesConsumed()); + else + SendWriteReply(context, NetErrorToPepperError(net_result)); + + write_buffer_ = NULL; + write_buffer_base_ = NULL; +} + +void PepperTCPSocketMessageFilter::OnAcceptCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(pending_accept_); + + pending_accept_ = false; + + if (net_result != net::OK) { + SendAcceptError(context, NetErrorToPepperError(net_result)); + return; + } + + DCHECK(accepted_socket_.get()); + + net::IPEndPoint ip_end_point_local; + PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress; + PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress; + + int32_t pp_result = + NetErrorToPepperError(accepted_socket_->GetLocalAddress( + &ip_end_point_local)); + if (pp_result != PP_OK) { + SendAcceptError(context, pp_result); + return; + } + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + accepted_address_.address(), + accepted_address_.port(), + &remote_addr)) { + SendAcceptError(context, PP_ERROR_ADDRESS_INVALID); + return; + } + + // |factory_| is guaranteed to be non-NULL here. Only those instances created + // in CONNECTED state have a NULL |factory_|, while getting here requires + // LISTENING state. + scoped_ptr<ppapi::host::ResourceHost> host = + factory_->CreateAcceptedTCPSocket( + instance_, version_, accepted_socket_.Pass()); + if (!host) { + SendAcceptError(context, PP_ERROR_NOSPACE); + return; + } + int pending_host_id = ppapi_host_->AddPendingResourceHost(host.Pass()); + if (pending_host_id) + SendAcceptReply(context, PP_OK, pending_host_id, local_addr, remote_addr); + else + SendAcceptError(context, PP_ERROR_NOSPACE); +} + +void PepperTCPSocketMessageFilter::SendBindReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_BindReply(local_addr)); +} + +void PepperTCPSocketMessageFilter::SendBindError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendBindReply(context, pp_error, NetAddressPrivateImpl::kInvalidNetAddress); +} + +void PepperTCPSocketMessageFilter::SendConnectReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_ConnectReply(local_addr, remote_addr)); +} + +void PepperTCPSocketMessageFilter::SendConnectError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendConnectReply(context, + pp_error, + NetAddressPrivateImpl::kInvalidNetAddress, + NetAddressPrivateImpl::kInvalidNetAddress); +} + +void PepperTCPSocketMessageFilter::SendSSLHandshakeReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + ppapi::PPB_X509Certificate_Fields certificate_fields; + if (pp_result == PP_OK) { + // Our socket is guaranteed to be an SSL socket if we get here. + net::SSLInfo ssl_info; + ssl_socket_->GetSSLInfo(&ssl_info); + if (ssl_info.cert.get()) { + pepper_socket_utils::GetCertificateFields(*ssl_info.cert.get(), + &certificate_fields); + } + } + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_SSLHandshakeReply(certificate_fields)); +} + +void PepperTCPSocketMessageFilter::SendReadReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const std::string& data) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_ReadReply(data)); +} + +void PepperTCPSocketMessageFilter::SendReadError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendReadReply(context, pp_error, std::string()); +} + +void PepperTCPSocketMessageFilter::SendWriteReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_WriteReply()); +} + +void PepperTCPSocketMessageFilter::SendListenReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_ListenReply()); +} + +void PepperTCPSocketMessageFilter::SendAcceptReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_AcceptReply( + pending_host_id, local_addr, remote_addr)); +} + +void PepperTCPSocketMessageFilter::SendAcceptError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendAcceptReply(context, pp_error, 0, + NetAddressPrivateImpl::kInvalidNetAddress, + NetAddressPrivateImpl::kInvalidNetAddress); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h new file mode 100644 index 00000000000..fb34b988d3c --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h @@ -0,0 +1,223 @@ +// Copyright 2013 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_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/pepper/ssl_context_helper.h" +#include "content/common/content_export.h" +#include "net/base/address_list.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_socket.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/ppb_tcp_socket.h" +#include "ppapi/c/private/ppb_net_address_private.h" +#include "ppapi/host/resource_message_filter.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" + +namespace net { +enum AddressFamily; +class DrainableIOBuffer; +class IOBuffer; +class SingleRequestHostResolver; +class SSLClientSocket; +} + +namespace ppapi { +class SocketOptionData; + +namespace host { +class PpapiHost; +struct ReplyMessageContext; +} +} + +namespace content { + +class BrowserPpapiHostImpl; +class ContentBrowserPepperHostFactory; +class ResourceContext; + +class CONTENT_EXPORT PepperTCPSocketMessageFilter + : public ppapi::host::ResourceMessageFilter { + public: + PepperTCPSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, + BrowserPpapiHostImpl* host, + PP_Instance instance, + ppapi::TCPSocketVersion version); + + // Used for creating already connected sockets. + PepperTCPSocketMessageFilter( + BrowserPpapiHostImpl* host, + PP_Instance instance, + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket); + + static size_t GetNumInstances(); + + private: + virtual ~PepperTCPSocketMessageFilter(); + + // ppapi::host::ResourceMessageFilter overrides. + virtual scoped_refptr<base::TaskRunner> OverrideTaskRunnerForMessage( + const IPC::Message& message) OVERRIDE; + virtual int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) OVERRIDE; + + int32_t OnMsgBind(const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr); + int32_t OnMsgConnect(const ppapi::host::HostMessageContext* context, + const std::string& host, + uint16_t port); + int32_t OnMsgConnectWithNetAddress( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr); + int32_t OnMsgSSLHandshake( + const ppapi::host::HostMessageContext* context, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs); + int32_t OnMsgRead(const ppapi::host::HostMessageContext* context, + int32_t bytes_to_read); + int32_t OnMsgWrite(const ppapi::host::HostMessageContext* context, + const std::string& data); + int32_t OnMsgListen(const ppapi::host::HostMessageContext* context, + int32_t backlog); + int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context); + int32_t OnMsgClose(const ppapi::host::HostMessageContext* context); + int32_t OnMsgSetOption(const ppapi::host::HostMessageContext* context, + PP_TCPSocket_Option name, + const ppapi::SocketOptionData& value); + + void DoBind(const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr); + void DoConnect(const ppapi::host::ReplyMessageContext& context, + const std::string& host, + uint16_t port, + ResourceContext* resource_context); + void DoConnectWithNetAddress( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr); + void DoWrite(const ppapi::host::ReplyMessageContext& context); + void DoListen(const ppapi::host::ReplyMessageContext& context, + int32_t backlog); + + void OnResolveCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void StartConnect(const ppapi::host::ReplyMessageContext& context); + + void OnConnectCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnSSLHandshakeCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnReadCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnWriteCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnAcceptCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + + void SendBindReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr); + void SendBindError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + void SendConnectReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + void SendConnectError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + void SendSSLHandshakeReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendReadReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const std::string& data); + void SendReadError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + void SendWriteReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendListenReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendAcceptReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + void SendAcceptError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + + bool IsPrivateAPI() const { + return version_ == ppapi::TCP_SOCKET_VERSION_PRIVATE; + } + + // The following fields are used on both the UI and IO thread. + const ppapi::TCPSocketVersion version_; + + // The following fields are used only on the UI thread. + const bool external_plugin_; + + int render_process_id_; + int render_view_id_; + + // The following fields are used only on the IO thread. + // Non-owning ptr. + ppapi::host::PpapiHost* ppapi_host_; + // Non-owning ptr. + ContentBrowserPepperHostFactory* factory_; + PP_Instance instance_; + + ppapi::TCPSocketState state_; + bool end_of_file_reached_; + + // This is the address requested to bind. Please note that this is not the + // bound address. For example, |bind_input_addr_| may have port set to 0. + // It is used to check permission for listening. + PP_NetAddress_Private bind_input_addr_; + + scoped_ptr<net::SingleRequestHostResolver> resolver_; + + // |address_list_| may store multiple addresses when + // PPB_TCPSocket_Private.Connect() is used, which involves name resolution. + // In that case, we will try each address in the list until a connection is + // successfully established. + net::AddressList address_list_; + // Where we are in the above list. + size_t address_index_; + + // Non-null unless an SSL connection is requested. + scoped_ptr<net::TCPSocket> socket_; + // Non-null if an SSL connection is requested. + scoped_ptr<net::SSLClientSocket> ssl_socket_; + + scoped_refptr<net::IOBuffer> read_buffer_; + + // TCPSocket::Write() may not always write the full buffer, but we would + // rather have our DoWrite() do so whenever possible. To do this, we may have + // to call the former multiple times for each of the latter. This entails + // using a DrainableIOBuffer, which requires an underlying base IOBuffer. + scoped_refptr<net::IOBuffer> write_buffer_base_; + scoped_refptr<net::DrainableIOBuffer> write_buffer_; + scoped_refptr<SSLContextHelper> ssl_context_helper_; + + bool pending_accept_; + scoped_ptr<net::TCPSocket> accepted_socket_; + net::IPEndPoint accepted_address_; + + DISALLOW_COPY_AND_ASSIGN(PepperTCPSocketMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc index 7453fd2886a..3127ed278fd 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc @@ -28,8 +28,8 @@ #include "ppapi/shared_impl/private/net_address_private_impl.h" #include "ppapi/shared_impl/socket_option_data.h" -using ppapi::host::NetErrorToPepperError; using ppapi::NetAddressPrivateImpl; +using ppapi::host::NetErrorToPepperError; namespace { @@ -173,7 +173,7 @@ int32_t PepperUDPSocketMessageFilter::OnMsgBind( pepper_socket_utils::CreateSocketPermissionRequest( SocketPermissionRequest::UDP_BIND, addr); if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, render_process_id_, + &request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; } @@ -236,7 +236,7 @@ int32_t PepperUDPSocketMessageFilter::OnMsgSendTo( pepper_socket_utils::CreateSocketPermissionRequest( SocketPermissionRequest::UDP_SEND_TO, addr); if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, render_process_id_, + &request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; } diff --git a/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc new file mode 100644 index 00000000000..3b92e6d0c16 --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc @@ -0,0 +1,30 @@ +// Copyright 2013 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/ssl_context_helper.h" + +#include "net/cert/cert_verifier.h" +#include "net/http/transport_security_state.h" + +namespace content { + +SSLContextHelper::SSLContextHelper() { +} + +SSLContextHelper::~SSLContextHelper() { +} + +net::CertVerifier* SSLContextHelper::GetCertVerifier() { + if (!cert_verifier_) + cert_verifier_.reset(net::CertVerifier::CreateDefault()); + return cert_verifier_.get(); +} + +net::TransportSecurityState* SSLContextHelper::GetTransportSecurityState() { + if (!transport_security_state_) + transport_security_state_.reset(new net::TransportSecurityState()); + return transport_security_state_.get(); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/ssl_context_helper.h b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.h new file mode 100644 index 00000000000..f1da2a6ee1d --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_SSL_CONTEXT_HELPER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/ssl/ssl_config_service.h" + +namespace net { +class CertVerifier; +class TransportSecurityState; +} + +namespace content { + +class SSLContextHelper : public base::RefCounted<SSLContextHelper> { + public: + SSLContextHelper(); + + net::CertVerifier* GetCertVerifier(); + net::TransportSecurityState* GetTransportSecurityState(); + const net::SSLConfig& ssl_config() { return ssl_config_; } + + private: + friend class base::RefCounted<SSLContextHelper>; + + ~SSLContextHelper(); + + // This is lazily created. Users should use GetCertVerifier to retrieve it. + scoped_ptr<net::CertVerifier> cert_verifier_; + // This is lazily created. Users should use GetTransportSecurityState to + // retrieve it. + scoped_ptr<net::TransportSecurityState> transport_security_state_; + + // The default SSL configuration settings are used, as opposed to Chrome's SSL + // settings. + net::SSLConfig ssl_config_; + + DISALLOW_COPY_AND_ASSIGN(SSLContextHelper); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_ diff --git a/chromium/content/browser/renderer_host/render_frame_host_impl.cc b/chromium/content/browser/renderer_host/render_frame_host_impl.cc index ea511d0b8c3..ec621c10dca 100644 --- a/chromium/content/browser/renderer_host/render_frame_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_frame_host_impl.cc @@ -4,31 +4,64 @@ #include "content/browser/renderer_host/render_frame_host_impl.h" +#include "base/containers/hash_tables.h" +#include "base/lazy_instance.h" #include "content/browser/renderer_host/render_view_host_impl.h" namespace content { +// The (process id, routing id) pair that identifies one RenderFrame. +typedef std::pair<int32, int32> RenderFrameHostID; +typedef base::hash_map<RenderFrameHostID, RenderFrameHostImpl*> + RoutingIDFrameMap; +static base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map = + LAZY_INSTANCE_INITIALIZER; + +// static +RenderFrameHostImpl* RenderFrameHostImpl::FromID( + int process_id, int routing_id) { + RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer(); + RoutingIDFrameMap::iterator it = frames->find( + RenderFrameHostID(process_id, routing_id)); + return it == frames->end() ? NULL : it->second; +} + RenderFrameHostImpl::RenderFrameHostImpl( RenderViewHostImpl* render_view_host, int routing_id, - bool swapped_out) + bool is_swapped_out) : render_view_host_(render_view_host), - routing_id_(routing_id) { + routing_id_(routing_id), + is_swapped_out_(is_swapped_out) { + GetProcess()->AddRoute(routing_id_, this); + g_routing_id_frame_map.Get().insert(std::make_pair( + RenderFrameHostID(GetProcess()->GetID(), routing_id_), + this)); } RenderFrameHostImpl::~RenderFrameHostImpl() { + GetProcess()->RemoveRoute(routing_id_); + g_routing_id_frame_map.Get().erase( + RenderFrameHostID(GetProcess()->GetID(), routing_id_)); + } bool RenderFrameHostImpl::Send(IPC::Message* message) { - // Use the RenderViewHost object to send the message. It inherits it from - // RenderWidgetHost, which ultimately uses the current process's |Send|. - return render_view_host_->Send(message); + return GetProcess()->Send(message); } bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) { - // Pass the message up to the RenderViewHost, until we have enough - // infrastructure to start processing messages in this object. - return render_view_host_->OnMessageReceived(msg); + return false; +} + +void RenderFrameHostImpl::Init() { + GetProcess()->ResumeRequestsForView(routing_id()); +} + +RenderProcessHost* RenderFrameHostImpl::GetProcess() const { + // TODO(ajwong): This should return its own process once cross-process + // subframe navigations are supported. + return render_view_host_->GetProcess(); } } // namespace content diff --git a/chromium/content/browser/renderer_host/render_frame_host_impl.h b/chromium/content/browser/renderer_host/render_frame_host_impl.h index bb94c8e63c2..78df3331efd 100644 --- a/chromium/content/browser/renderer_host/render_frame_host_impl.h +++ b/chromium/content/browser/renderer_host/render_frame_host_impl.h @@ -10,14 +10,16 @@ namespace content { +class RenderProcessHost; class RenderViewHostImpl; class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost { public: - RenderFrameHostImpl( - RenderViewHostImpl* render_view_host, - int routing_id, - bool swapped_out); + static RenderFrameHostImpl* FromID(int process_id, int routing_id); + + RenderFrameHostImpl(RenderViewHostImpl* render_view_host, + int routing_id, + bool is_swapped_out); virtual ~RenderFrameHostImpl(); // IPC::Sender @@ -26,12 +28,16 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost { // IPC::Listener virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; - int routing_id() { return routing_id_; } + void Init(); + RenderProcessHost* GetProcess() const; + int routing_id() const { return routing_id_; } private: - RenderViewHostImpl* render_view_host_; + bool is_swapped_out() { return is_swapped_out_; } + RenderViewHostImpl* render_view_host_; // Not owned. Outlives this object. int routing_id_; + bool is_swapped_out_; DISALLOW_COPY_AND_ASSIGN(RenderFrameHostImpl); }; diff --git a/chromium/content/browser/renderer_host/render_process_host_browsertest.cc b/chromium/content/browser/renderer_host/render_process_host_browsertest.cc index 9ee8036e044..de64924d717 100644 --- a/chromium/content/browser/renderer_host/render_process_host_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_process_host_browsertest.cc @@ -8,7 +8,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/test/test_notification_tracker.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" diff --git a/chromium/content/browser/renderer_host/render_process_host_impl.cc b/chromium/content/browser/renderer_host/render_process_host_impl.cc index 2dd784a4e69..73cb5e11f9c 100644 --- a/chromium/content/browser/renderer_host/render_process_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_process_host_impl.cc @@ -52,6 +52,7 @@ #include "content/browser/fileapi/chrome_blob_storage_context.h" #include "content/browser/fileapi/fileapi_message_filter.h" #include "content/browser/geolocation/geolocation_dispatcher_host.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/gpu/shader_disk_cache.h" @@ -60,6 +61,7 @@ #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" #include "content/browser/loader/resource_message_filter.h" #include "content/browser/loader/resource_scheduler_filter.h" +#include "content/browser/media/android/browser_demuxer_android.h" #include "content/browser/media/media_internals.h" #include "content/browser/mime_registry_message_filter.h" #include "content/browser/plugin_service_impl.h" @@ -91,6 +93,8 @@ #include "content/browser/renderer_host/socket_stream_dispatcher_host.h" #include "content/browser/renderer_host/text_input_client_message_filter.h" #include "content/browser/resolve_proxy_msg_helper.h" +#include "content/browser/service_worker/service_worker_context.h" +#include "content/browser/service_worker/service_worker_dispatcher_host.h" #include "content/browser/speech/input_tag_speech_dispatcher_host.h" #include "content/browser/speech/speech_recognition_dispatcher_host.h" #include "content/browser/storage_partition_impl.h" @@ -111,6 +115,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host_factory.h" #include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_constants.h" @@ -118,8 +123,6 @@ #include "content/public/common/process_type.h" #include "content/public/common/result_codes.h" #include "content/public/common/url_constants.h" -#include "content/renderer/render_process_impl.h" -#include "content/renderer/render_thread_impl.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_logging.h" @@ -129,6 +132,7 @@ #include "net/url_request/url_request_context_getter.h" #include "ppapi/shared_impl/ppapi_switches.h" #include "ui/base/ui_base_switches.h" +#include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" #include "webkit/browser/fileapi/sandbox_file_system_backend.h" #include "webkit/common/resource_type.h" @@ -157,8 +161,6 @@ static const char* kSiteProcessMapKeyName = "content_site_process_map"; namespace content { namespace { -base::MessageLoop* g_in_process_thread; - void CacheShaderInfo(int32 id, base::FilePath path) { ShaderCacheFactory::GetInstance()->SetCacheInfo(id, path); } @@ -167,88 +169,30 @@ void RemoveShaderInfo(int32 id) { ShaderCacheFactory::GetInstance()->RemoveCacheInfo(id); } -} // namespace - -#if !defined(CHROME_MULTIPLE_DLL) - -// This class creates the IO thread for the renderer when running in -// single-process mode. It's not used in multi-process mode. -class RendererMainThread : public base::Thread { - public: - explicit RendererMainThread(const std::string& channel_id) - : Thread("Chrome_InProcRendererThread"), - channel_id_(channel_id) { - } - - virtual ~RendererMainThread() { - Stop(); - } - - protected: - virtual void Init() OVERRIDE { - render_process_.reset(new RenderProcessImpl()); - new RenderThreadImpl(channel_id_); - g_in_process_thread = message_loop(); - } - - virtual void CleanUp() OVERRIDE { - g_in_process_thread = NULL; - render_process_.reset(); - - // It's a little lame to manually set this flag. But the single process - // RendererThread will receive the WM_QUIT. We don't need to assert on - // this thread, so just force the flag manually. - // If we want to avoid this, we could create the InProcRendererThread - // directly with _beginthreadex() rather than using the Thread class. - // We used to set this flag in the Init function above. However there - // other threads like WebThread which are created by this thread - // which resets this flag. Please see Thread::StartWithOptions. Setting - // this flag to true in Cleanup works around these problems. - SetThreadWasQuitProperly(true); - } - - private: - std::string channel_id_; - scoped_ptr<RenderProcess> render_process_; - - DISALLOW_COPY_AND_ASSIGN(RendererMainThread); -}; - -#endif - -namespace { - -// Helper class that we pass to ResourceMessageFilter so that it can find the -// right net::URLRequestContext for a request. -class RendererURLRequestContextSelector - : public ResourceMessageFilter::URLRequestContextSelector { - public: - RendererURLRequestContextSelector(BrowserContext* browser_context, - int render_child_id) - : request_context_(browser_context->GetRequestContextForRenderProcess( - render_child_id)), - media_request_context_( - browser_context->GetMediaRequestContextForRenderProcess( - render_child_id)) { - } - - virtual net::URLRequestContext* GetRequestContext( - ResourceType::Type resource_type) OVERRIDE { - net::URLRequestContextGetter* request_context = request_context_.get(); - // If the request has resource type of ResourceType::MEDIA, we use a request - // context specific to media for handling it because these resources have - // specific needs for caching. - if (resource_type == ResourceType::MEDIA) - request_context = media_request_context_.get(); - return request_context->GetURLRequestContext(); - } - - private: - virtual ~RendererURLRequestContextSelector() {} +net::URLRequestContext* GetRequestContext( + scoped_refptr<net::URLRequestContextGetter> request_context, + scoped_refptr<net::URLRequestContextGetter> media_request_context, + ResourceType::Type resource_type) { + // If the request has resource type of ResourceType::MEDIA, we use a request + // context specific to media for handling it because these resources have + // specific needs for caching. + if (resource_type == ResourceType::MEDIA) + return media_request_context->GetURLRequestContext(); + return request_context->GetURLRequestContext(); +} - scoped_refptr<net::URLRequestContextGetter> request_context_; - scoped_refptr<net::URLRequestContextGetter> media_request_context_; -}; +void GetContexts( + ResourceContext* resource_context, + scoped_refptr<net::URLRequestContextGetter> request_context, + scoped_refptr<net::URLRequestContextGetter> media_request_context, + const ResourceHostMsg_Request& request, + ResourceContext** resource_context_out, + net::URLRequestContext** request_context_out) { + *resource_context_out = resource_context; + *request_context_out = + GetRequestContext(request_context, media_request_context, + request.resource_type); +} // the global list of all renderer processes base::LazyInstance<IDMap<RenderProcessHost> >::Leaky @@ -339,6 +283,20 @@ class RendererSandboxedProcessLauncherDelegate } // namespace +RendererMainThreadFactoryFunction g_renderer_main_thread_factory = NULL; + +void RenderProcessHost::RegisterRendererMainThreadFactory( + RendererMainThreadFactoryFunction create) { + g_renderer_main_thread_factory = create; +} + +base::MessageLoop* g_in_process_thread; + +base::MessageLoop* + RenderProcessHostImpl::GetInProcessRendererThreadForTesting() { + return g_in_process_thread; +} + // Stores the maximum number of renderer processes the content module can // create. static size_t g_max_renderer_count_override = 0; @@ -511,15 +469,14 @@ bool RenderProcessHostImpl::Init() { CreateMessageFilters(); // Single-process mode not supported in multiple-dll mode currently. -#if !defined(CHROME_MULTIPLE_DLL) - if (run_renderer_in_process()) { + if (run_renderer_in_process() && g_renderer_main_thread_factory) { // Crank up a thread and run the initialization there. With the way that // messages flow between the browser and renderer, this thread is required // to prevent a deadlock in single-process mode. Since the primordial // thread in the renderer process runs the WebKit code and can sometimes // make blocking calls to the UI thread (i.e. this thread), they need to run // on separate threads. - in_process_renderer_.reset(new RendererMainThread(channel_id)); + in_process_renderer_.reset(g_renderer_main_thread_factory(channel_id)); base::Thread::Options options; #if defined(OS_WIN) && !defined(OS_MACOSX) @@ -532,10 +489,10 @@ bool RenderProcessHostImpl::Init() { #endif in_process_renderer_->StartWithOptions(options); + g_in_process_thread = in_process_renderer_->message_loop(); + OnProcessLaunched(); // Fake a callback that the process is ready. - } else -#endif // !CHROME_MULTIPLE_DLL - { + } else { // Build command line for renderer. We call AppendRendererCommandLine() // first so the process type argument will appear first. CommandLine* cmd_line = new CommandLine(renderer_path); @@ -552,7 +509,7 @@ bool RenderProcessHostImpl::Init() { new RendererSandboxedProcessLauncherDelegate, #elif defined(OS_POSIX) renderer_prefix.empty(), - base::EnvironmentVector(), + base::EnvironmentMap(), channel_->TakeClientFileDescriptor(), #endif cmd_line, @@ -604,12 +561,21 @@ void RenderProcessHostImpl::CreateMessageFilters() { BrowserContext* browser_context = GetBrowserContext(); ResourceContext* resource_context = browser_context->GetResourceContext(); + scoped_refptr<net::URLRequestContextGetter> request_context( + browser_context->GetRequestContextForRenderProcess(GetID())); + scoped_refptr<net::URLRequestContextGetter> media_request_context( + browser_context->GetMediaRequestContextForRenderProcess(GetID())); + + ResourceMessageFilter::GetContextsCallback get_contexts_callback( + base::Bind(&GetContexts, browser_context->GetResourceContext(), + request_context, media_request_context)); + ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( - GetID(), PROCESS_TYPE_RENDERER, resource_context, + GetID(), PROCESS_TYPE_RENDERER, storage_partition_impl_->GetAppCacheService(), ChromeBlobStorageContext::GetFor(browser_context), storage_partition_impl_->GetFileSystemContext(), - new RendererURLRequestContextSelector(browser_context, GetID())); + get_contexts_callback); channel_->AddFilter(resource_message_filter); MediaStreamManager* media_stream_manager = @@ -617,13 +583,16 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new AudioInputRendererHost( audio_manager, media_stream_manager, - BrowserMainLoop::GetInstance()->audio_mirroring_manager())); + BrowserMainLoop::GetInstance()->audio_mirroring_manager(), + BrowserMainLoop::GetInstance()->user_input_monitor())); channel_->AddFilter(new AudioRendererHost( - GetID(), audio_manager, + GetID(), + audio_manager, BrowserMainLoop::GetInstance()->audio_mirroring_manager(), - media_internals, media_stream_manager)); + media_internals, + media_stream_manager)); channel_->AddFilter( - new MIDIHost(BrowserMainLoop::GetInstance()->midi_manager())); + new MIDIHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager())); channel_->AddFilter(new MIDIDispatcherHost(GetID(), browser_context)); channel_->AddFilter(new VideoCaptureHost(media_stream_manager)); channel_->AddFilter(new AppCacheDispatcherHost( @@ -636,6 +605,8 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new IndexedDBDispatcherHost( GetID(), storage_partition_impl_->GetIndexedDBContext())); + channel_->AddFilter(new ServiceWorkerDispatcherHost( + storage_partition_impl_->GetServiceWorkerContext())); if (IsGuest()) { if (!g_browser_plugin_geolocation_context.Get().get()) { g_browser_plugin_geolocation_context.Get() = @@ -660,8 +631,6 @@ void RenderProcessHostImpl::CreateMessageFilters() { new DeviceRequestMessageFilter(resource_context, media_stream_manager)); #endif #if defined(ENABLE_PLUGINS) - // TODO(raymes): PepperMessageFilter should be removed from here. - channel_->AddFilter(new PepperMessageFilter(GetID(), browser_context)); channel_->AddFilter(new PepperRendererConnection(GetID())); #endif #if defined(ENABLE_INPUT_SPEECH) @@ -685,12 +654,19 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new TextInputClientMessageFilter(GetID())); #elif defined(OS_WIN) channel_->AddFilter(new FontCacheDispatcher()); +#elif defined(OS_ANDROID) + browser_demuxer_android_ = new BrowserDemuxerAndroid(); + channel_->AddFilter(browser_demuxer_android_); #endif + SocketStreamDispatcherHost::GetRequestContextCallback + request_context_callback( + base::Bind(&GetRequestContext, request_context, + media_request_context)); + SocketStreamDispatcherHost* socket_stream_dispatcher_host = - new SocketStreamDispatcherHost(GetID(), - new RendererURLRequestContextSelector(browser_context, GetID()), - resource_context); + new SocketStreamDispatcherHost( + GetID(), request_context_callback, resource_context); channel_->AddFilter(socket_stream_dispatcher_host); channel_->AddFilter(new WorkerMessageFilter( @@ -856,6 +832,15 @@ void RenderProcessHostImpl::AppendRendererCommandLine( field_trial_states); } + if (content::IsThreadedCompositingEnabled()) + command_line->AppendSwitch(switches::kEnableThreadedCompositing); + + if (content::IsDelegatedRendererEnabled()) + command_line->AppendSwitch(switches::kEnableDelegatedRenderer); + + if (content::IsDeadlineSchedulingEnabled()) + command_line->AppendSwitch(switches::kEnableDeadlineScheduling); + GetContentClient()->browser()->AppendExtraCommandLineSwitches( command_line, GetID()); @@ -875,6 +860,8 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kAudioBufferSize, switches::kAuditAllHandles, switches::kAuditHandles, + switches::kBlockCrossSiteDocuments, + switches::kDirectNPAPIRequests, switches::kDisable3DAPIs, switches::kDisableAcceleratedCompositing, switches::kDisableAcceleratedVideoDecode, @@ -882,6 +869,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableAudio, switches::kDisableBreakpad, switches::kDisableDatabases, + switches::kDisableDeadlineScheduling, switches::kDisableDelegatedRenderer, switches::kDisableDesktopNotifications, switches::kDisableDeviceOrientation, @@ -894,7 +882,6 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableHistogramCustomizer, switches::kDisableLocalStorage, switches::kDisableLogging, - switches::kDisableNewDialogStyle, switches::kDisableSeccompFilterSandbox, switches::kDisableSessionStorage, switches::kDisableSharedWorkers, @@ -908,7 +895,9 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableWebAudio, #if defined(ENABLE_WEBRTC) switches::kDisableDeviceEnumeration, - switches::kEnableSCTPDataChannels, + switches::kDisableSCTPDataChannels, + switches::kDisableWebRtcHWDecoding, + switches::kDisableWebRtcHWEncoding, #endif switches::kEnableWebAnimationsCSS, switches::kEnableWebAnimationsSVG, @@ -921,10 +910,15 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableBrowserInputController, switches::kEnableBrowserPluginForAllViewTypes, switches::kEnableDCHECK, + switches::kEnableDeadlineScheduling, switches::kEnableDelegatedRenderer, switches::kEnableEncryptedMedia, switches::kDisableLegacyEncryptedMedia, switches::kOverrideEncryptedMediaCanPlayType, +#if defined(OS_ANDROID) + switches::kEnableMediaDrm, + switches::kMediaDrmEnableNonCompositing, +#endif switches::kEnableExperimentalWebPlatformFeatures, switches::kEnableFixedLayout, switches::kEnableDeferredImageDecoding, @@ -932,7 +926,12 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableGPUClientLogging, switches::kEnableGpuClientTracing, switches::kEnableGpuBenchmarking, +#if defined(OS_WIN) + switches::kEnableHighResolutionTime, +#endif + switches::kEnableMP3StreamParser, switches::kEnableMemoryBenchmarking, + switches::kEnableOverlayFullscreenVideo, switches::kEnableOverlayScrollbars, switches::kEnableSkiaBenchmarking, switches::kEnableLogging, @@ -942,13 +941,11 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( #if defined(ENABLE_WEBRTC) switches::kEnableWebRtcAecRecordings, switches::kEnableWebRtcTcpServerSocket, - switches::kEnableWebRtcHWDecoding, #endif switches::kDisableWebKitMediaSource, switches::kEnableOverscrollNotifications, switches::kEnableStrictSiteIsolation, switches::kDisableFullScreen, - switches::kEnableNewDialogStyle, #if defined(ENABLE_PLUGINS) switches::kEnablePepperTesting, switches::kDisablePepper3d, @@ -981,7 +978,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableViewport, switches::kEnableInbandTextTracks, switches::kEnableOpusPlayback, - switches::kEnableVp8AlphaPlayback, + switches::kDisableVp8AlphaPlayback, switches::kEnableEac3Playback, switches::kForceDeviceScaleFactor, switches::kFullMemoryCrashReport, @@ -1030,19 +1027,21 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kWebCoreLogChannels, switches::kEnableWebGLDraftExtensions, switches::kEnableHTMLImports, + switches::kEnableInputModeAttribute, switches::kTraceToConsole, - switches::kEnableDeviceMotion, -#if defined(OS_ANDROID) switches::kDisableDeviceMotion, -#endif // Please keep these in alphabetical order. Compositor switches here should // also be added to chrome/browser/chromeos/login/chrome_restart_request.cc. cc::switches::kBackgroundColorInsteadOfCheckerboard, cc::switches::kCompositeToMailbox, cc::switches::kDisableCompositedAntialiasing, cc::switches::kDisableImplSidePainting, + cc::switches::kDisableLCDText, + cc::switches::kDisableMapImage, cc::switches::kDisableThreadedAnimation, cc::switches::kEnableImplSidePainting, + cc::switches::kEnableLCDText, + cc::switches::kEnableMapImage, cc::switches::kEnablePartialSwap, cc::switches::kEnablePerTilePainting, cc::switches::kEnablePinchVirtualViewport, @@ -1066,7 +1065,6 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( cc::switches::kTopControlsHideThreshold, cc::switches::kTopControlsShowThreshold, cc::switches::kTraceOverdraw, - cc::switches::kUseMapImage, }; renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, arraysize(kSwitchNames)); @@ -1249,7 +1247,6 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) { // Adding single handlers for your service here is fine, but once your // service needs more than one handler, please extract them into a new // message filter and add that filter to CreateMessageFilters(). - IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP_EX() if (!msg_is_ok) { @@ -1613,11 +1610,6 @@ void RenderProcessHostImpl::RegisterProcessHostForSite( map->RegisterProcess(site, process); } -base::MessageLoop* - RenderProcessHostImpl::GetInProcessRendererThreadForTesting() { - return g_in_process_thread; -} - void RenderProcessHostImpl::ProcessDied(bool already_dead) { // Our child process has died. If we didn't expect it, it's a crash. // In any case, we need to let everyone know it's gone. @@ -1661,10 +1653,11 @@ void RenderProcessHostImpl::ProcessDied(bool already_dead) { int RenderProcessHostImpl::GetActiveViewCount() { int num_active_views = 0; - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { // Count only RenderWidgetHosts in this process. - if (widgets[i]->GetProcess()->GetID() == GetID()) + if (widget->GetProcess()->GetID() == GetID()) num_active_views++; } return num_active_views; @@ -1797,17 +1790,17 @@ void RenderProcessHostImpl::OnCompositorSurfaceBuffersSwappedNoHost( void RenderProcessHostImpl::OnGpuSwitching() { // We are updating all widgets including swapped out ones. - RenderWidgetHost::List widgets = - RenderWidgetHostImpl::GetAllRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { - if (!widgets[i]->IsRenderView()) + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHostImpl::GetAllRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { + if (!widget->IsRenderView()) continue; // Skip widgets in other processes. - if (widgets[i]->GetProcess()->GetID() != GetID()) + if (widget->GetProcess()->GetID() != GetID()) continue; - RenderViewHost* rvh = RenderViewHost::From(widgets[i]); + RenderViewHost* rvh = RenderViewHost::From(widget); rvh->UpdateWebkitPreferences(rvh->GetWebkitPreferences()); } } diff --git a/chromium/content/browser/renderer_host/render_process_host_impl.h b/chromium/content/browser/renderer_host/render_process_host_impl.h index 72ec846303d..0e9ae1b2ad1 100644 --- a/chromium/content/browser/renderer_host/render_process_host_impl.h +++ b/chromium/content/browser/renderer_host/render_process_host_impl.h @@ -33,6 +33,7 @@ class Size; } namespace content { +class BrowserDemuxerAndroid; class GpuMessageFilter; class PeerConnectionTrackerHost; class RendererMainThread; @@ -177,6 +178,12 @@ class CONTENT_EXPORT RenderProcessHostImpl static base::MessageLoop* GetInProcessRendererThreadForTesting(); +#if defined(OS_ANDROID) + const scoped_refptr<BrowserDemuxerAndroid>& browser_demuxer_android() { + return browser_demuxer_android_; + } +#endif + protected: // A proxy for our IPC::Channel that lives on the IO thread (see // browser_process.h) @@ -264,10 +271,8 @@ class CONTENT_EXPORT RenderProcessHostImpl // This is used to clear our cache five seconds after the last use. base::DelayTimer<RenderProcessHostImpl> cached_dibs_cleaner_; -#if !defined(CHROME_MULTIPLE_DLL) // Used in single-process mode. - scoped_ptr<RendererMainThread> in_process_renderer_; -#endif + scoped_ptr<base::Thread> in_process_renderer_; // True after Init() has been called. We can't just check channel_ because we // also reset that in the case of process termination. @@ -324,6 +329,10 @@ class CONTENT_EXPORT RenderProcessHostImpl // Forwards power state messages to the renderer process. PowerMonitorMessageBroadcaster power_monitor_broadcaster_; +#if defined(OS_ANDROID) + scoped_refptr<BrowserDemuxerAndroid> browser_demuxer_android_; +#endif + DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImpl); }; diff --git a/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc b/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc index 87d31dca88c..81537181532 100644 --- a/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc +++ b/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc @@ -30,6 +30,8 @@ #include "content/child/webkitplatformsupport_impl.h" #include "content/common/font_config_ipc_linux.h" #include "content/common/sandbox_linux.h" +#include "content/common/set_process_title.h" +#include "content/public/common/content_switches.h" #include "skia/ext/skia_utils_base.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/linux/WebFontInfo.h" @@ -71,6 +73,15 @@ class SandboxIPCProcess { // positioning, so we pass the current setting through to WebKit. WebFontInfo::setSubpixelPositioning( gfx::GetDefaultWebkitSubpixelPositioning()); + + CommandLine& command_line = *CommandLine::ForCurrentProcess(); + command_line.AppendSwitchASCII(switches::kProcessType, + switches::kSandboxIPCProcess); + + // Update the process title. The argv was already cached by the call to + // SetProcessTitleFromCommandLine in content_main_runner.cc, so we can pass + // NULL here (we don't have the original argv at this point). + SetProcessTitleFromCommandLine(NULL); } ~SandboxIPCProcess(); diff --git a/chromium/content/browser/renderer_host/render_view_host_browsertest.cc b/chromium/content/browser/renderer_host/render_view_host_browsertest.cc index 4255d055ce1..20fec727501 100644 --- a/chromium/content/browser/renderer_host/render_view_host_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_view_host_browsertest.cc @@ -13,7 +13,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_paths.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/host_port_pair.h" diff --git a/chromium/content/browser/renderer_host/render_view_host_delegate.h b/chromium/content/browser/renderer_host/render_view_host_delegate.h index 5ce2a44d716..997452aeb54 100644 --- a/chromium/content/browser/renderer_host/render_view_host_delegate.h +++ b/chromium/content/browser/renderer_host/render_view_host_delegate.h @@ -50,6 +50,7 @@ class PageState; class RenderViewHost; class RenderViewHostDelegateView; class SessionStorageNamespace; +class SiteInstance; class WebContents; class WebContentsImpl; struct ContextMenuParams; @@ -58,7 +59,8 @@ struct GlobalRequestID; struct NativeWebKeyboardEvent; struct Referrer; struct RendererPreferences; -class SiteInstance; +struct ResourceRedirectDetails; +struct ResourceRequestDetails; // // RenderViewHostDelegate @@ -151,8 +153,8 @@ class CONTENT_EXPORT RenderViewHostDelegate { // The RenderView processed a redirect during a provisional load. // // TODO(creis): Remove this method and have the pre-rendering code listen to - // the ResourceDispatcherHost's RESOURCE_RECEIVED_REDIRECT notification - // instead. See http://crbug.com/78512. + // WebContentsObserver::DidGetRedirectForResourceRequest instead. + // See http://crbug.com/78512. virtual void DidRedirectProvisionalLoad( RenderViewHost* render_view_host, int32 page_id, @@ -164,6 +166,14 @@ class CONTENT_EXPORT RenderViewHostDelegate { RenderViewHost* render_view_host, const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params) {} + // A response has been received for a resource request. + virtual void DidGetResourceResponseStart( + const ResourceRequestDetails& details) {} + + // A redirect was received while requesting a resource. + virtual void DidGetRedirectForResourceRequest( + const ResourceRedirectDetails& details) {} + // The RenderView was navigated to a different page. virtual void DidNavigate(RenderViewHost* render_view_host, const ViewHostMsg_FrameNavigate_Params& params) {} @@ -299,6 +309,7 @@ class CONTENT_EXPORT RenderViewHostDelegate { // 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_before_unload, bool is_during_unload) {} // Notification that a previously unresponsive renderer has become diff --git a/chromium/content/browser/renderer_host/render_view_host_factory.cc b/chromium/content/browser/renderer_host/render_view_host_factory.cc index 20d15d38f0c..5cffd134b8e 100644 --- a/chromium/content/browser/renderer_host/render_view_host_factory.cc +++ b/chromium/content/browser/renderer_host/render_view_host_factory.cc @@ -19,14 +19,15 @@ RenderViewHost* RenderViewHostFactory::Create( RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out) { + bool swapped_out, + bool hidden) { if (factory_) { return factory_->CreateRenderViewHost(instance, delegate, widget_delegate, routing_id, main_frame_routing_id, swapped_out); } return new RenderViewHostImpl(instance, delegate, widget_delegate, routing_id, - main_frame_routing_id, swapped_out); + main_frame_routing_id, swapped_out, hidden); } // static diff --git a/chromium/content/browser/renderer_host/render_view_host_factory.h b/chromium/content/browser/renderer_host/render_view_host_factory.h index 6226187af53..b0466355d49 100644 --- a/chromium/content/browser/renderer_host/render_view_host_factory.h +++ b/chromium/content/browser/renderer_host/render_view_host_factory.h @@ -29,7 +29,8 @@ class RenderViewHostFactory { RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out); + bool swapped_out, + bool hidden); // Returns true if there is currently a globally-registered factory. static bool has_factory() { diff --git a/chromium/content/browser/renderer_host/render_view_host_impl.cc b/chromium/content/browser/renderer_host/render_view_host_impl.cc index 56801c03c18..68204fb746a 100644 --- a/chromium/content/browser/renderer_host/render_view_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_view_host_impl.cc @@ -51,6 +51,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_view_host_observer.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/content_constants.h" @@ -72,7 +73,7 @@ #if defined(OS_MACOSX) #include "content/browser/renderer_host/popup_menu_helper_mac.h" #elif defined(OS_ANDROID) -#include "media/base/android/media_player_manager.h" +#include "content/browser/media/android/browser_media_player_manager.h" #endif using base::TimeDelta; @@ -116,11 +117,7 @@ g_created_callbacks = LAZY_INSTANCE_INITIALIZER; // static RenderViewHost* RenderViewHost::FromID(int render_process_id, int render_view_id) { - RenderWidgetHost* widget = - RenderWidgetHost::FromID(render_process_id, render_view_id); - if (!widget || !widget->IsRenderView()) - return NULL; - return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(widget)); + return RenderViewHostImpl::FromID(render_process_id, render_view_id); } // static @@ -143,8 +140,11 @@ void RenderViewHost::FilterURL(const RenderProcessHost* process, // static RenderViewHostImpl* RenderViewHostImpl::FromID(int render_process_id, int render_view_id) { - return static_cast<RenderViewHostImpl*>( - RenderViewHost::FromID(render_process_id, render_view_id)); + RenderWidgetHost* widget = + RenderWidgetHost::FromID(render_process_id, render_view_id); + if (!widget || !widget->IsRenderView()) + return NULL; + return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(widget)); } RenderViewHostImpl::RenderViewHostImpl( @@ -153,8 +153,12 @@ RenderViewHostImpl::RenderViewHostImpl( RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out) - : RenderWidgetHostImpl(widget_delegate, instance->GetProcess(), routing_id), + bool swapped_out, + bool hidden) + : RenderWidgetHostImpl(widget_delegate, + instance->GetProcess(), + routing_id, + hidden), delegate_(delegate), instance_(static_cast<SiteInstanceImpl*>(instance)), waiting_for_drag_context_response_(false), @@ -191,7 +195,7 @@ RenderViewHostImpl::RenderViewHostImpl( instance_->increment_active_view_count(); #if defined(OS_ANDROID) - media_player_manager_ = media::MediaPlayerManager::Create(this); + media_player_manager_ = BrowserMediaPlayerManager::Create(this); #endif } @@ -259,6 +263,7 @@ bool RenderViewHostImpl::CreateRenderView( // Ensure the RenderView sets its opener correctly. params.opener_route_id = opener_route_id; params.swapped_out = is_swapped_out_; + params.hidden = is_hidden(); params.next_page_id = next_page_id; GetWebScreenInfo(¶ms.screen_info); params.accessibility_mode = accessibility_mode(); @@ -338,7 +343,7 @@ void RenderViewHostImpl::Navigate(const ViewMsg_Navigate_Params& params) { // // WebKit doesn't send throb notifications for JavaScript URLs, so we // don't want to either. - if (!params.url.SchemeIs(chrome::kJavaScriptScheme)) + if (!params.url.SchemeIs(kJavaScriptScheme)) delegate_->DidStartLoading(this); FOR_EACH_OBSERVER(RenderViewHostObserver, observers_, Navigate(params.url)); @@ -479,9 +484,10 @@ void RenderViewHostImpl::WasSwappedOut() { // Count the number of active widget hosts for the process, which // is equivalent to views using the process as of this writing. - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { - if (widgets[i]->GetProcess()->GetID() == GetProcess()->GetID()) + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { + if (widget->GetProcess()->GetID() == GetProcess()->GetID()) ++views; } @@ -736,7 +742,8 @@ void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg, // 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); + delegate_->RendererUnresponsive( + this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_); } void RenderViewHostImpl::DragSourceEndedAt( @@ -763,6 +770,12 @@ void RenderViewHostImpl::DragSourceSystemDragEnded() { } void RenderViewHostImpl::AllowBindings(int bindings_flags) { + // Never grant any bindings to browser plugin guests. + if (GetProcess()->IsGuest()) { + NOTREACHED() << "Never grant bindings to a guest process."; + return; + } + // Ensure we aren't granting WebUI bindings to a process that has already // been used for non-privileged views. if (bindings_flags & BINDINGS_POLICY_WEB_UI && @@ -777,12 +790,6 @@ void RenderViewHostImpl::AllowBindings(int bindings_flags) { return; } - // Never grant any bindings to browser plugin guests. - if (GetProcess()->IsGuest()) { - NOTREACHED() << "Never grant bindings to a guest process."; - return; - } - if (bindings_flags & BINDINGS_POLICY_WEB_UI) { ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings( GetProcess()->GetID()); @@ -999,8 +1006,7 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) { OnDidAccessInitialDocument) IPC_MESSAGE_HANDLER(ViewHostMsg_DomOperationResponse, OnDomOperationResponse) - IPC_MESSAGE_HANDLER(AccessibilityHostMsg_Notifications, - OnAccessibilityNotifications) + IPC_MESSAGE_HANDLER(AccessibilityHostMsg_Events, OnAccessibilityEvents) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED( handled = RenderWidgetHostImpl::OnMessageReceived(msg)) @@ -1016,6 +1022,11 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) { return handled; } +void RenderViewHostImpl::Init() { + RenderWidgetHostImpl::Init(); + main_render_frame_host()->Init(); +} + void RenderViewHostImpl::Shutdown() { // If we are being run modally (see RunModal), then we need to cleanup. if (run_modal_reply_msg_) { @@ -1239,8 +1250,6 @@ void RenderViewHostImpl::OnNavigate(const IPC::Message& msg) { FilterURL(policy, process, false, &(*it)); } FilterURL(policy, process, true, &validated_params.searchable_form_url); - FilterURL(policy, process, true, &validated_params.password_form.origin); - FilterURL(policy, process, true, &validated_params.password_form.action); // Without this check, the renderer can trick the browser into using // filenames it can't access in a future session restore. @@ -1354,6 +1363,8 @@ void RenderViewHostImpl::OnContextMenu(const ContextMenuParams& params) { void RenderViewHostImpl::OnToggleFullscreen(bool enter_fullscreen) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); delegate_->ToggleFullscreenMode(enter_fullscreen); + // We need to notify the contents that its fullscreen state has changed. This + // is done as part of the resize message. WasResized(); } @@ -1399,7 +1410,7 @@ void RenderViewHostImpl::OnDidChangeNumWheelEvents(int count) { void RenderViewHostImpl::OnSelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { if (view_) view_->SelectionChanged(text, offset, range); } @@ -1464,7 +1475,7 @@ void RenderViewHostImpl::OnStartDragging( ChildProcessSecurityPolicyImpl::GetInstance(); // Allow drag of Javascript URLs to enable bookmarklet drag to bookmark bar. - if (!filtered_data.url.SchemeIs(chrome::kJavaScriptScheme)) + if (!filtered_data.url.SchemeIs(kJavaScriptScheme)) FilterURL(policy, process, true, &filtered_data.url); FilterURL(policy, process, false, &filtered_data.html_base_url); // Filter out any paths that the renderer didn't have access to. This prevents @@ -1522,6 +1533,7 @@ void RenderViewHostImpl::OnAddMessageToConsole( const string16& source_id) { if (delegate_->AddMessageToConsole(level, message, line_no, source_id)) return; + // Pass through log level only on WebUI pages to limit console spew. int32 resolved_level = HasWebUIScheme(delegate_->GetURL()) ? level : 0; @@ -1595,7 +1607,7 @@ void RenderViewHostImpl::OnClosePageACK() { void RenderViewHostImpl::NotifyRendererUnresponsive() { delegate_->RendererUnresponsive( - this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); + this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_); } void RenderViewHostImpl::NotifyRendererResponsive() { @@ -1772,9 +1784,8 @@ void RenderViewHostImpl::SetAltErrorPageURL(const GURL& url) { void RenderViewHostImpl::ExitFullscreen() { RejectMouseLockOrUnlockIfNecessary(); - // We need to notify the contents that its fullscreen state has changed. This - // is done as part of the resize message. - WasResized(); + // Notify delegate_ and renderer of fullscreen state change. + OnToggleFullscreen(false); } WebPreferences RenderViewHostImpl::GetWebkitPreferences() { @@ -1789,7 +1800,7 @@ void RenderViewHostImpl::DisownOpener() { } void RenderViewHostImpl::SetAccessibilityCallbackForTesting( - const base::Callback<void(AccessibilityNotification)>& callback) { + const base::Callback<void(WebKit::WebAXEvent)>& callback) { accessibility_testing_callback_ = callback; } @@ -1871,10 +1882,6 @@ void RenderViewHostImpl::ExecutePluginActionAtLocation( Send(new ViewMsg_PluginActionAt(GetRoutingID(), location, action)); } -void RenderViewHostImpl::DisassociateFromPopupCount() { - Send(new ViewMsg_DisassociateFromPopupCount(GetRoutingID())); -} - void RenderViewHostImpl::NotifyMoveOrResizeStarted() { Send(new ViewMsg_MoveOrResizeStarted(GetRoutingID())); } @@ -1883,13 +1890,13 @@ void RenderViewHostImpl::StopFinding(StopFindAction action) { Send(new ViewMsg_StopFinding(GetRoutingID(), action)); } -void RenderViewHostImpl::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderViewHostImpl::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (view_ && !is_swapped_out_) - view_->OnAccessibilityNotifications(params); + view_->OnAccessibilityEvents(params); // Always send an ACK or the renderer can be in a bad state. - Send(new AccessibilityMsg_Notifications_ACK(GetRoutingID())); + Send(new AccessibilityMsg_Events_ACK(GetRoutingID())); // The rest of this code is just for testing; bail out if we're not // in that mode. @@ -1897,10 +1904,10 @@ void RenderViewHostImpl::OnAccessibilityNotifications( return; for (unsigned i = 0; i < params.size(); i++) { - const AccessibilityHostMsg_NotificationParams& param = params[i]; - AccessibilityNotification src_type = param.notification_type; - if (src_type == AccessibilityNotificationLayoutComplete || - src_type == AccessibilityNotificationLoadComplete) { + const AccessibilityHostMsg_EventParams& param = params[i]; + WebKit::WebAXEvent src_type = param.event_type; + if (src_type == WebKit::WebAXEventLayoutComplete || + src_type == WebKit::WebAXEventLoadComplete) { MakeAccessibilityNodeDataTree(param.nodes, &accessibility_tree_); } accessibility_testing_callback_.Run(src_type); @@ -1953,7 +1960,7 @@ void RenderViewHostImpl::OnShowDesktopNotification( // allows unwanted cross-domain access. GURL url = params.contents_url; if (params.is_html && - (url.SchemeIs(chrome::kJavaScriptScheme) || + (url.SchemeIs(kJavaScriptScheme) || url.SchemeIs(chrome::kFileScheme))) { return; } @@ -2024,6 +2031,11 @@ void RenderViewHostImpl::OnShowPopup( } #endif +RenderFrameHostImpl* RenderViewHostImpl::main_render_frame_host() const { + DCHECK_EQ(GetProcess(), main_render_frame_host_->GetProcess()); + return main_render_frame_host_.get(); +} + void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { // We update the number of RenderViews in a SiteInstance when the // swapped out status of this RenderView gets flipped. diff --git a/chromium/content/browser/renderer_host/render_view_host_impl.h b/chromium/content/browser/renderer_host/render_view_host_impl.h index 20caaf5ea73..c4bfb7876d1 100644 --- a/chromium/content/browser/renderer_host/render_view_host_impl.h +++ b/chromium/content/browser/renderer_host/render_view_host_impl.h @@ -18,22 +18,22 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/site_instance_impl.h" #include "content/common/accessibility_node_data.h" -#include "content/common/accessibility_notification.h" #include "content/common/drag_event_source_info.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/render_view_host.h" #include "content/public/common/javascript_message_type.h" #include "content/public/common/window_container_type.h" #include "net/base/load_states.h" -#include "third_party/skia/include/core/SkColor.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebPopupType.h" #include "third_party/WebKit/public/web/WebTextDirection.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/base/window_open_disposition.h" class SkBitmap; class ViewMsg_Navigate; -struct AccessibilityHostMsg_NotificationParams; +struct AccessibilityHostMsg_EventParams; struct MediaPlayerAction; struct ViewHostMsg_CreateWindow_Params; struct ViewHostMsg_DidFailProvisionalLoadWithError_Params; @@ -48,19 +48,17 @@ namespace base { class ListValue; } -namespace ui { +namespace gfx { class Range; -struct SelectedFileInfo; } -#if defined(OS_ANDROID) -namespace media { -class MediaPlayerManager; +namespace ui { +struct SelectedFileInfo; } -#endif namespace content { +class BrowserMediaPlayerManager; class ChildProcessSecurityPolicyImpl; class PageState; class RenderFrameHostImpl; @@ -110,7 +108,8 @@ class CONTENT_EXPORT RenderViewHostImpl // |routing_id| could be a valid route id, or it could be MSG_ROUTING_NONE, in // which case RenderWidgetHost will create a new one. |swapped_out| indicates // whether the view should initially be swapped out (e.g., for an opener - // frame being rendered by another process). + // frame being rendered by another process). |hidden| indicates whether the + // view is initially hidden or visible. // // The |session_storage_namespace| parameter allows multiple render views and // WebContentses to share the same session storage (part of the WebStorage @@ -123,7 +122,8 @@ class CONTENT_EXPORT RenderViewHostImpl RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out); + bool swapped_out, + bool hidden); virtual ~RenderViewHostImpl(); // RenderViewHost implementation. @@ -131,7 +131,6 @@ class CONTENT_EXPORT RenderViewHostImpl virtual void ClearFocusedNode() OVERRIDE; virtual void ClosePage() OVERRIDE; virtual void CopyImageAt(int x, int y) OVERRIDE; - virtual void DisassociateFromPopupCount() OVERRIDE; virtual void DesktopNotificationPermissionRequestDone( int callback_context) OVERRIDE; virtual void DesktopNotificationPostDisplay(int callback_context) OVERRIDE; @@ -354,6 +353,7 @@ class CONTENT_EXPORT RenderViewHostImpl } // RenderWidgetHost public overrides. + virtual void Init() OVERRIDE; virtual void Shutdown() OVERRIDE; virtual bool IsRenderView() const OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; @@ -388,7 +388,7 @@ class CONTENT_EXPORT RenderViewHostImpl #endif #if defined(OS_ANDROID) - media::MediaPlayerManager* media_player_manager() { + BrowserMediaPlayerManager* media_player_manager() { return media_player_manager_; } @@ -417,7 +417,7 @@ class CONTENT_EXPORT RenderViewHostImpl // renderer process, and the accessibility tree it sent can be // retrieved using accessibility_tree_for_testing(). void SetAccessibilityCallbackForTesting( - const base::Callback<void(AccessibilityNotification)>& callback); + const base::Callback<void(WebKit::WebAXEvent)>& callback); // Only valid if SetAccessibilityCallbackForTesting was called and // the callback was run at least once. Returns a snapshot of the @@ -526,7 +526,7 @@ class CONTENT_EXPORT RenderViewHostImpl void OnDidChangeNumWheelEvents(int count); void OnSelectionChanged(const string16& text, size_t offset, - const ui::Range& range); + const gfx::Range& range); void OnSelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params); void OnPasteFromSelectionClipboard(); @@ -562,8 +562,8 @@ class CONTENT_EXPORT RenderViewHostImpl const base::TimeTicks& renderer_before_unload_end_time); void OnClosePageACK(); void OnSwapOutACK(); - void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params); + void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params); void OnScriptEvalResponse(int id, const base::ListValue& result); void OnDidZoomURL(double zoom_level, bool remember, const GURL& url); void OnRequestDesktopNotificationPermission(const GURL& origin, @@ -584,6 +584,11 @@ class CONTENT_EXPORT RenderViewHostImpl private: friend class TestRenderViewHost; FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, BasicRenderFrameHost); + FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, RoutingIdSane); + + // TODO(nasko): Remove this accessor once RenderFrameHost moves into the frame + // tree. + RenderFrameHostImpl* main_render_frame_host() const; // Sets whether this RenderViewHost is swapped out in favor of another, // and clears any waiting state that is no longer relevant. @@ -678,8 +683,7 @@ class CONTENT_EXPORT RenderViewHostImpl std::map<int, JavascriptResultCallback> javascript_callbacks_; // Accessibility callback for testing. - base::Callback<void(AccessibilityNotification)> - accessibility_testing_callback_; + base::Callback<void(WebKit::WebAXEvent)> accessibility_testing_callback_; // The most recently received accessibility tree - for testing only. AccessibilityNodeDataTreeNode accessibility_tree_; @@ -699,7 +703,7 @@ class CONTENT_EXPORT RenderViewHostImpl #if defined(OS_ANDROID) // Manages all the android mediaplayer objects and handling IPCs for video. // This class inherits from RenderViewHostObserver. - media::MediaPlayerManager* media_player_manager_; + BrowserMediaPlayerManager* media_player_manager_; #endif DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl); diff --git a/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc b/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc index 4053d6593f2..55785d07859 100644 --- a/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc @@ -25,7 +25,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" @@ -86,7 +86,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -153,7 +153,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the window to open. Shell* new_shell = new_shell_observer.GetShell(); - EXPECT_EQ("/files/title2.html", new_shell->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + new_shell->web_contents()->GetVisibleURL().path()); // Wait for the cross-site transition in the new tab to finish. WaitForLoadStop(new_shell->web_contents()); @@ -207,7 +208,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, Shell* new_shell = new_shell_observer.GetShell(); // Opens in new window. - EXPECT_EQ("/files/title2.html", new_shell->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + new_shell->web_contents()->GetVisibleURL().path()); // Wait for the cross-site transition in the new tab to finish. WaitForLoadStop(new_shell->web_contents()); @@ -262,7 +264,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the cross-site transition in the new tab to finish. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/title2.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -308,7 +310,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Opens in same window. EXPECT_EQ(1u, Shell::windows().size()); - EXPECT_EQ("/files/title2.html", shell()->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + shell()->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> noref_site_instance( @@ -376,7 +379,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new tab to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -421,7 +424,13 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Test that setting the opener to null in a window affects cross-process // navigations, including those to existing entries. http://crbug.com/156669. -IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, DisownOpener) { +// Flaky on windows: http://crbug.com/291249 +#if defined(OS_WIN) +#define MAYBE_DisownOpener DISABLED_DisownOpener +#else +#define MAYBE_DisownOpener DisownOpener +#endif +IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, MAYBE_DisownOpener) { // Start two servers with different sites. ASSERT_TRUE(test_server()->Start()); net::SpawnedTestServer https_server( @@ -456,7 +465,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, DisownOpener) { // Wait for the navigation in the new tab to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/title2.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -562,7 +571,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // send it to post_message.html on a different site. WebContents* foo_contents = new_shell->web_contents(); WaitForLoadStop(foo_contents); - EXPECT_EQ("/files/navigate_opener.html", foo_contents->GetURL().path()); + EXPECT_EQ("/files/navigate_opener.html", + foo_contents->GetLastCommittedURL().path()); NavigateToURL(new_shell, https_server.GetURL("files/post_message.html")); scoped_refptr<SiteInstance> foo_site_instance( foo_contents->GetSiteInstance()); @@ -581,7 +591,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, Shell* new_shell2 = new_shell_observer2.GetShell(); WebContents* new_contents = new_shell2->web_contents(); WaitForLoadStop(new_contents); - EXPECT_EQ("/files/title2.html", new_contents->GetURL().path()); + EXPECT_EQ("/files/title2.html", new_contents->GetLastCommittedURL().path()); NavigateToURL(new_shell2, test_server()->GetURL("files/post_message.html")); EXPECT_EQ(orig_site_instance, new_contents->GetSiteInstance()); RenderViewHostManager* new_manager = @@ -698,7 +708,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -765,7 +775,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> opened_site_instance( @@ -829,7 +839,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ClickLinkAfter204Error) { scoped_refptr<SiteInstance> post_nav_site_instance( shell()->web_contents()->GetSiteInstance()); EXPECT_EQ(orig_site_instance, post_nav_site_instance); - EXPECT_EQ("/nocontent", shell()->web_contents()->GetURL().path()); + EXPECT_EQ("/nocontent", + shell()->web_contents()->GetVisibleURL().path()); EXPECT_EQ("/files/click-noreferrer-links.html", shell()->web_contents()->GetController(). GetLastCommittedEntry()->GetVirtualURL().path()); @@ -847,7 +858,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ClickLinkAfter204Error) { // Opens in same tab. EXPECT_EQ(1u, Shell::windows().size()); - EXPECT_EQ("/files/title2.html", shell()->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + shell()->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> noref_site_instance( @@ -1073,7 +1085,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new tab to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); RenderViewHost* rvh = new_shell->web_contents()->GetRenderViewHost(); @@ -1104,7 +1116,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, } EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); EXPECT_EQ(rvh, new_shell->web_contents()->GetRenderViewHost()); @@ -1187,7 +1199,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, LeakingRenderViewHosts) { // view-source URL, we create a new SiteInstance. RenderViewHost* blank_rvh = shell()->web_contents()->GetRenderViewHost(); SiteInstance* blank_site_instance = blank_rvh->GetSiteInstance(); - EXPECT_EQ(shell()->web_contents()->GetURL(), GURL::EmptyGURL()); + EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL::EmptyGURL()); EXPECT_EQ(blank_site_instance->GetSiteURL(), GURL::EmptyGURL()); rvh_observers.AddObserverToRVH(blank_rvh); @@ -1269,7 +1281,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/title1.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. EXPECT_EQ(orig_site_instance, new_shell->web_contents()->GetSiteInstance()); @@ -1286,7 +1298,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Make sure it ends up at the right page. WaitForLoadStop(shell()->web_contents()); EXPECT_EQ(https_server.GetURL("files/title1.html"), - shell()->web_contents()->GetURL()); + shell()->web_contents()->GetLastCommittedURL()); EXPECT_EQ(new_site_instance, shell()->web_contents()->GetSiteInstance()); } diff --git a/chromium/content/browser/renderer_host/render_view_host_unittest.cc b/chromium/content/browser/renderer_host/render_view_host_unittest.cc index a38e0446f3d..f4d694fae42 100644 --- a/chromium/content/browser/renderer_host/render_view_host_unittest.cc +++ b/chromium/content/browser/renderer_host/render_view_host_unittest.cc @@ -288,4 +288,11 @@ TEST_F(RenderViewHostTest, NavigationWithBadHistoryItemFiles) { EXPECT_EQ(1, process()->bad_msg_count()); } +TEST_F(RenderViewHostTest, RoutingIdSane) { + EXPECT_EQ(test_rvh()->GetProcess(), + test_rvh()->main_render_frame_host()->GetProcess()); + EXPECT_NE(test_rvh()->GetRoutingID(), + test_rvh()->main_render_frame_host()->routing_id()); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc index 9f11299b1a6..bfdf6a5450a 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc @@ -7,7 +7,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" @@ -46,8 +46,14 @@ class RenderWidgetHostBrowserTest : public ContentBrowserTest { base::FilePath test_dir_; }; +// Disabled on Windows and CrOS because it is flaky: crbug.com/272379. +#if defined(OS_WIN) || defined(OS_CHROMEOS) +#define MAYBE_GetSnapshotFromRendererTest DISABLED_GetSnapshotFromRendererTest +#else +#define MAYBE_GetSnapshotFromRendererTest GetSnapshotFromRendererTest +#endif IN_PROC_BROWSER_TEST_F(RenderWidgetHostBrowserTest, - GetSnapshotFromRendererTest) { + MAYBE_GetSnapshotFromRendererTest) { base::RunLoop run_loop; NavigateToURL(shell(), GURL(net::FilePathToFileURL( diff --git a/chromium/content/browser/renderer_host/render_widget_host_impl.cc b/chromium/content/browser/renderer_host/render_widget_host_impl.cc index edd21fe5987..4fce8d8cb2d 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_impl.cc @@ -22,12 +22,14 @@ #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_ack.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/gpu/gpu_process_host_ui_shim.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/backing_store.h" #include "content/browser/renderer_host/backing_store_manager.h" #include "content/browser/renderer_host/dip_util.h" +#include "content/browser/renderer_host/input/buffered_input_router.h" #include "content/browser/renderer_host/input/immediate_input_router.h" #include "content/browser/renderer_host/overscroll_controller.h" #include "content/browser/renderer_host/render_process_host_impl.h" @@ -40,10 +42,10 @@ #include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" @@ -51,8 +53,8 @@ #include "skia/ext/image_operations.h" #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/web/WebCompositionUnderline.h" -#include "ui/base/events/event.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/size_conversions.h" #include "ui/gfx/skbitmap_operations.h" #include "ui/gfx/vector2d_conversions.h" @@ -91,15 +93,50 @@ const int kPaintMsgTimeoutMS = 50; base::LazyInstance<std::vector<RenderWidgetHost::CreatedCallback> > g_created_callbacks = LAZY_INSTANCE_INITIALIZER; -} // namespace - - typedef std::pair<int32, int32> RenderWidgetHostID; typedef base::hash_map<RenderWidgetHostID, RenderWidgetHostImpl*> RoutingIDWidgetMap; -static base::LazyInstance<RoutingIDWidgetMap> g_routing_id_widget_map = +base::LazyInstance<RoutingIDWidgetMap> g_routing_id_widget_map = LAZY_INSTANCE_INITIALIZER; +// Implements the RenderWidgetHostIterator interface. It keeps a list of +// RenderWidgetHosts, and makes sure it returns a live RenderWidgetHost at each +// iteration (or NULL if there isn't any left). +class RenderWidgetHostIteratorImpl : public RenderWidgetHostIterator { + public: + RenderWidgetHostIteratorImpl() + : current_index_(0) { + } + + virtual ~RenderWidgetHostIteratorImpl() { + } + + void Add(RenderWidgetHost* host) { + hosts_.push_back(RenderWidgetHostID(host->GetProcess()->GetID(), + host->GetRoutingID())); + } + + // RenderWidgetHostIterator: + virtual RenderWidgetHost* GetNextHost() OVERRIDE { + RenderWidgetHost* host = NULL; + while (current_index_ < hosts_.size() && !host) { + RenderWidgetHostID id = hosts_[current_index_]; + host = RenderWidgetHost::FromID(id.first, id.second); + ++current_index_; + } + return host; + } + + private: + std::vector<RenderWidgetHostID> hosts_; + size_t current_index_; + + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostIteratorImpl); +}; + +} // namespace + + // static void RenderWidgetHost::RemoveAllBackingStores() { BackingStoreManager::RemoveAllBackingStores(); @@ -115,7 +152,8 @@ size_t RenderWidgetHost::BackingStoreMemorySize() { RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, - int routing_id) + int routing_id, + bool hidden) : view_(NULL), renderer_initialized_(false), hung_renderer_delay_ms_(kHungRendererDelayMs), @@ -124,11 +162,12 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, routing_id_(routing_id), surface_id_(0), is_loading_(false), - is_hidden_(false), + is_hidden_(hidden), is_fullscreen_(false), is_accelerated_compositing_active_(false), repaint_ack_pending_(false), resize_ack_pending_(false), + screen_info_out_of_date_(false), overdraw_bottom_height_(0.f), should_auto_resize_(false), waiting_for_screen_rects_ack_(false), @@ -175,9 +214,11 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, g_routing_id_widget_map.Get().insert(std::make_pair( RenderWidgetHostID(process->GetID(), routing_id_), this)); process_->AddRoute(routing_id_, this); - // Because the widget initializes as is_hidden_ == false, - // tell the process host that we're alive. - process_->WidgetRestored(); + + // If we're initially visible, tell the process host that we're alive. + // Otherwise we'll notify the process host when we are first shown. + if (!hidden) + process_->WidgetRestored(); accessibility_mode_ = BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode(); @@ -185,7 +226,8 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, for (size_t i = 0; i < g_created_callbacks.Get().size(); i++) g_created_callbacks.Get().at(i).Run(this); - input_router_.reset(new ImmediateInputRouter(process, this, routing_id_)); + input_router_.reset( + new ImmediateInputRouter(process_, this, this, routing_id_)); #if defined(USE_AURA) bool overscroll_enabled = CommandLine::ForCurrentProcess()-> @@ -222,6 +264,7 @@ RenderWidgetHost* RenderWidgetHost::FromID( RenderWidgetHostImpl* RenderWidgetHostImpl::FromID( int32 process_id, int32 routing_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer(); RoutingIDWidgetMap::iterator it = widgets->find( RenderWidgetHostID(process_id, routing_id)); @@ -229,8 +272,8 @@ RenderWidgetHostImpl* RenderWidgetHostImpl::FromID( } // static -std::vector<RenderWidgetHost*> RenderWidgetHost::GetRenderWidgetHosts() { - std::vector<RenderWidgetHost*> hosts; +scoped_ptr<RenderWidgetHostIterator> RenderWidgetHost::GetRenderWidgetHosts() { + RenderWidgetHostIteratorImpl* hosts = new RenderWidgetHostIteratorImpl(); RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer(); for (RoutingIDWidgetMap::const_iterator it = widgets->begin(); it != widgets->end(); @@ -238,28 +281,31 @@ std::vector<RenderWidgetHost*> RenderWidgetHost::GetRenderWidgetHosts() { RenderWidgetHost* widget = it->second; if (!widget->IsRenderView()) { - hosts.push_back(widget); + hosts->Add(widget); continue; } // Add only active RenderViewHosts. RenderViewHost* rvh = RenderViewHost::From(widget); if (!static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out()) - hosts.push_back(widget); + hosts->Add(widget); } - return hosts; + + return scoped_ptr<RenderWidgetHostIterator>(hosts); } // static -std::vector<RenderWidgetHost*> RenderWidgetHostImpl::GetAllRenderWidgetHosts() { - std::vector<RenderWidgetHost*> hosts; +scoped_ptr<RenderWidgetHostIterator> +RenderWidgetHostImpl::GetAllRenderWidgetHosts() { + RenderWidgetHostIteratorImpl* hosts = new RenderWidgetHostIteratorImpl(); RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer(); for (RoutingIDWidgetMap::const_iterator it = widgets->begin(); it != widgets->end(); ++it) { - hosts.push_back(it->second); + hosts->Add(it->second); } - return hosts; + + return scoped_ptr<RenderWidgetHostIterator>(hosts); } // static @@ -358,8 +404,8 @@ void RenderWidgetHostImpl::SendScreenRects() { } base::TimeDelta - RenderWidgetHostImpl::GetSyntheticScrollMessageInterval() const { - return smooth_scroll_gesture_controller_.GetSyntheticScrollMessageInterval(); + RenderWidgetHostImpl::GetSyntheticGestureMessageInterval() const { + return synthetic_gesture_controller_.GetSyntheticGestureMessageInterval(); } void RenderWidgetHostImpl::SetOverscrollControllerEnabled(bool enabled) { @@ -430,6 +476,7 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnUpdateIsDelayed) IPC_MESSAGE_HANDLER(ViewHostMsg_BeginSmoothScroll, OnBeginSmoothScroll) + IPC_MESSAGE_HANDLER(ViewHostMsg_BeginPinch, OnBeginPinch) IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnFocus) IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnBlur) IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnSetCursor) @@ -473,12 +520,15 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) { bool RenderWidgetHostImpl::Send(IPC::Message* msg) { if (IPC_MESSAGE_ID_CLASS(msg->type()) == InputMsgStart) - return input_router_->SendInput(msg); + return input_router_->SendInput(make_scoped_ptr(msg)); return process_->Send(msg); } void RenderWidgetHostImpl::WasHidden() { + if (is_hidden_) + return; + is_hidden_ = true; // Don't bother reporting hung state when we aren't active. @@ -499,7 +549,6 @@ void RenderWidgetHostImpl::WasHidden() { } void RenderWidgetHostImpl::WasShown() { - // When we create the widget, it is created as *not* hidden. if (!is_hidden_) return; is_hidden_ = false; @@ -548,8 +597,10 @@ void RenderWidgetHostImpl::WasShown() { } void RenderWidgetHostImpl::WasResized() { + // Skip if the |delegate_| has already been detached because + // it's web contents is being deleted. if (resize_ack_pending_ || !process_->HasConnection() || !view_ || - !renderer_initialized_ || should_auto_resize_) { + !renderer_initialized_ || should_auto_resize_ || !delegate_) { return; } @@ -565,7 +616,7 @@ void RenderWidgetHostImpl::WasResized() { bool size_changed = new_size != last_requested_size_; bool side_payload_changed = - !screen_info_.get() || + screen_info_out_of_date_ || old_physical_backing_size != physical_backing_size_ || was_fullscreen != is_fullscreen_ || old_overdraw_bottom_height != overdraw_bottom_height_; @@ -838,20 +889,20 @@ void RenderWidgetHostImpl::DonePaintingToBackingStore() { Send(new ViewMsg_UpdateRect_ACK(GetRoutingID())); } -void RenderWidgetHostImpl::ScheduleComposite() { +bool RenderWidgetHostImpl::ScheduleComposite() { if (is_hidden_ || !is_accelerated_compositing_active_ || - current_size_.IsEmpty()) { - return; + current_size_.IsEmpty() || repaint_ack_pending_ || + resize_ack_pending_ || view_being_painted_) { + return false; } // 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; - TRACE_EVENT_ASYNC_BEGIN0( - "renderer_host", "RenderWidgetHostImpl::repaint_ack_pending_", this); - Send(new ViewMsg_Repaint(routing_id_, current_size_)); - } + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + TRACE_EVENT_ASYNC_BEGIN0( + "renderer_host", "RenderWidgetHostImpl::repaint_ack_pending_", this); + Send(new ViewMsg_Repaint(routing_id_, current_size_)); + return true; } void RenderWidgetHostImpl::StartHangMonitorTimeout(TimeDelta delay) { @@ -1010,6 +1061,12 @@ void RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo( const MouseEventWithLatencyInfo& mouse_event) { TRACE_EVENT2("input", "RenderWidgetHostImpl::ForwardMouseEvent", "x", mouse_event.event.x, "y", mouse_event.event.y); + + for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) { + if (mouse_event_callbacks_[i].Run(mouse_event.event)) + return; + } + input_router_->SendMouseEvent(mouse_event); } @@ -1117,15 +1174,35 @@ ui::LatencyInfo RenderWidgetHostImpl::CreateRWHLatencyInfoIfNotExist( } -void RenderWidgetHostImpl::AddKeyboardListener(KeyboardListener* listener) { - keyboard_listeners_.AddObserver(listener); +void RenderWidgetHostImpl::AddKeyPressEventCallback( + const KeyPressEventCallback& callback) { + key_press_event_callbacks_.push_back(callback); +} + +void RenderWidgetHostImpl::RemoveKeyPressEventCallback( + const KeyPressEventCallback& callback) { + for (size_t i = 0; i < key_press_event_callbacks_.size(); ++i) { + if (key_press_event_callbacks_[i].Equals(callback)) { + key_press_event_callbacks_.erase( + key_press_event_callbacks_.begin() + i); + return; + } + } +} + +void RenderWidgetHostImpl::AddMouseEventCallback( + const MouseEventCallback& callback) { + mouse_event_callbacks_.push_back(callback); } -void RenderWidgetHostImpl::RemoveKeyboardListener( - KeyboardListener* listener) { - // Ensure that the element is actually an observer. - DCHECK(keyboard_listeners_.HasObserver(listener)); - keyboard_listeners_.RemoveObserver(listener); +void RenderWidgetHostImpl::RemoveMouseEventCallback( + const MouseEventCallback& callback) { + for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) { + if (mouse_event_callbacks_[i].Equals(callback)) { + mouse_event_callbacks_.erase(mouse_event_callbacks_.begin() + i); + return; + } + } } void RenderWidgetHostImpl::GetWebScreenInfo(WebKit::WebScreenInfo* result) { @@ -1134,6 +1211,7 @@ void RenderWidgetHostImpl::GetWebScreenInfo(WebKit::WebScreenInfo* result) { static_cast<RenderWidgetHostViewPort*>(GetView())->GetScreenInfo(result); else RenderWidgetHostViewPort::GetDefaultScreenInfo(result); + screen_info_out_of_date_ = false; } const NativeWebKeyboardEvent* @@ -1150,6 +1228,7 @@ void RenderWidgetHostImpl::NotifyScreenInfoChanged() { } void RenderWidgetHostImpl::InvalidateScreenInfo() { + screen_info_out_of_date_ = true; screen_info_.reset(); } @@ -1200,7 +1279,8 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status, waiting_for_screen_rects_ack_ = false; // Reset to ensure that input routing works with a new renderer. - input_router_.reset(new ImmediateInputRouter(process_, this, routing_id_)); + input_router_.reset( + new ImmediateInputRouter(process_, this, this, routing_id_)); if (overscroll_controller_) overscroll_controller_->Reset(); @@ -1262,7 +1342,7 @@ void RenderWidgetHostImpl::ImeSetComposition( void RenderWidgetHostImpl::ImeConfirmComposition( const string16& text, - const ui::Range& replacement_range, + const gfx::Range& replacement_range, bool keep_selection) { Send(new ViewMsg_ImeConfirmComposition( GetRoutingID(), text, replacement_range, keep_selection)); @@ -1482,7 +1562,9 @@ bool RenderWidgetHostImpl::OnSwapCompositorFrame( ack.gl_frame_data = frame->gl_frame_data.Pass(); ack.gl_frame_data->sync_point = 0; } else if (frame->delegated_frame_data) { - ack.resources.swap(frame->delegated_frame_data->resource_list); + cc::TransferableResource::ReturnResources( + frame->delegated_frame_data->resource_list, + &ack.resources); } else if (frame->software_frame_data) { ack.last_software_frame_id = frame->software_frame_data->id; } @@ -1665,7 +1747,14 @@ void RenderWidgetHostImpl::OnBeginSmoothScroll( const ViewHostMsg_BeginSmoothScroll_Params& params) { if (!view_) return; - smooth_scroll_gesture_controller_.BeginSmoothScroll(view_, params); + synthetic_gesture_controller_.BeginSmoothScroll(view_, params); +} + +void RenderWidgetHostImpl::OnBeginPinch( + const ViewHostMsg_BeginPinch_Params& params) { + if (!view_) + return; + synthetic_gesture_controller_.BeginPinch(view_, params); } void RenderWidgetHostImpl::OnFocus() { @@ -1689,15 +1778,15 @@ void RenderWidgetHostImpl::OnSetCursor(const WebCursor& cursor) { void RenderWidgetHostImpl::OnTextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (view_) - view_->TextInputTypeChanged(type, can_compose_inline, input_mode); + view_->TextInputTypeChanged(type, input_mode, can_compose_inline); } #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) void RenderWidgetHostImpl::OnImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { if (view_) view_->ImeCompositionRangeChanged(range, character_bounds); @@ -1879,10 +1968,8 @@ bool RenderWidgetHostImpl::KeyPressListenersHandleEvent( if (event.skip_in_browser || event.type != WebKeyboardEvent::RawKeyDown) return false; - ObserverList<KeyboardListener>::Iterator it(keyboard_listeners_); - KeyboardListener* listener; - while ((listener = it.GetNext()) != NULL) { - if (listener->HandleKeyPressEvent(event)) + for (size_t i = 0; i < key_press_event_callbacks_.size(); i++) { + if (key_press_event_callbacks_[i].Run(event)) return true; } @@ -2067,6 +2154,12 @@ bool RenderWidgetHostImpl::OnSendGestureEventImmediately( return !IgnoreInputEvents(); } +void RenderWidgetHostImpl::SetNeedsFlush() { +} + +void RenderWidgetHostImpl::DidFlush() { +} + void RenderWidgetHostImpl::OnKeyboardEventAck( const NativeWebKeyboardEvent& event, InputEventAckState ack_result) { @@ -2118,13 +2211,13 @@ void RenderWidgetHostImpl::OnTouchEventAck( view_->ProcessAckedTouchEvent(event, ack_result); } -void RenderWidgetHostImpl::OnUnexpectedEventAck(bool bad_message) { - if (bad_message) { +void RenderWidgetHostImpl::OnUnexpectedEventAck(UnexpectedEventAckType type) { + if (type == BAD_ACK_MESSAGE) { RecordAction(UserMetricsAction("BadMessageTerminate_RWH2")); process_->ReceivedBadMessage(); + } else if (type == UNEXPECTED_EVENT_TYPE) { + suppress_next_char_events_ = false; } - - suppress_next_char_events_ = false; } const gfx::Vector2d& RenderWidgetHostImpl::GetLastScrollOffset() const { @@ -2144,10 +2237,6 @@ bool RenderWidgetHostImpl::ShouldForwardGestureEvent( return input_router_->ShouldForwardGestureEvent(gesture_event); } -bool RenderWidgetHostImpl::HasQueuedGestureEvents() const { - return input_router_->HasQueuedGestureEvents(); -} - void RenderWidgetHostImpl::StartUserGesture() { OnUserGesture(); } @@ -2331,6 +2420,19 @@ void RenderWidgetHostImpl::SendSwapCompositorFrameAck( route_id, output_surface_id, ack)); } +// static +void RenderWidgetHostImpl::SendReclaimCompositorResources( + int32 route_id, + uint32 output_surface_id, + int renderer_host_id, + const cc::CompositorFrameAck& ack) { + RenderProcessHost* host = RenderProcessHost::FromID(renderer_host_id); + if (!host) + return; + host->Send( + new ViewMsg_ReclaimCompositorResources(route_id, output_surface_id, ack)); +} + void RenderWidgetHostImpl::AcknowledgeSwapBuffersToRenderer() { if (!is_threaded_compositing_enabled_) Send(new ViewMsg_SwapBuffers_ACK(routing_id_)); diff --git a/chromium/content/browser/renderer_host/render_widget_host_impl.h b/chromium/content/browser/renderer_host/render_widget_host_impl.h index 362c6d7f793..613177c9a81 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_impl.h +++ b/chromium/content/browser/renderer_host/render_widget_host_impl.h @@ -23,8 +23,9 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "build/build_config.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" #include "content/browser/renderer_host/input/input_router_client.h" -#include "content/browser/renderer_host/smooth_scroll_gesture_controller.h" +#include "content/browser/renderer_host/synthetic_gesture_controller.h" #include "content/common/browser_rendering_stats.h" #include "content/common/view_message_enums.h" #include "content/port/browser/event_with_latency_info.h" @@ -34,7 +35,7 @@ #include "ipc/ipc_listener.h" #include "ui/base/ime/text_input_mode.h" #include "ui/base/ime/text_input_type.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gfx/native_widget_types.h" class WebCursor; @@ -53,9 +54,12 @@ class CompositorFrame; class CompositorFrameAck; } +namespace gfx { +class Range; +} + namespace ui { class KeyEvent; -class Range; } namespace WebKit { @@ -78,13 +82,14 @@ class MockRenderWidgetHost; class OverscrollController; class RenderWidgetHostDelegate; class RenderWidgetHostViewPort; -class SmoothScrollGestureController; +class SyntheticGestureController; struct EditCommand; // This implements the RenderWidgetHost interface that is exposed to // embedders of content, and adds things only visible to content. class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, public InputRouterClient, + public InputAckHandler, public IPC::Listener { public: // routing_id can be MSG_ROUTING_NONE, in which case the next available @@ -93,7 +98,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // |delegate| goes away. RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, - int routing_id); + int routing_id, + bool hidden); virtual ~RenderWidgetHostImpl(); // Similar to RenderWidgetHost::FromID, but returning the Impl object. @@ -102,14 +108,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // Returns all RenderWidgetHosts including swapped out ones for // internal use. The public interface // RendgerWidgetHost::GetRenderWidgetHosts only returns active ones. - // Keep in mind that there may be dependencies between these - // widgets. If a caller indirectly causes one of the widgets to be - // deleted while iterating over the list, the deleted widget will - // stay in the list and possibly causes a use-after-free. Take care - // to avoid deleting widgets as you iterate (e.g., see - // http://crbug.com/259859). TODO(nasko): Improve this interface to - // better prevent UaFs. - static std::vector<RenderWidgetHost*> GetAllRenderWidgetHosts(); + static scoped_ptr<RenderWidgetHostIterator> GetAllRenderWidgetHosts(); // Use RenderWidgetHostImpl::From(rwh) to downcast a // RenderWidgetHost to a RenderWidgetHostImpl. Internally, this @@ -172,8 +171,14 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, virtual void SetIgnoreInputEvents(bool ignore_input_events) OVERRIDE; virtual void Stop() OVERRIDE; virtual void WasResized() OVERRIDE; - virtual void AddKeyboardListener(KeyboardListener* listener) OVERRIDE; - virtual void RemoveKeyboardListener(KeyboardListener* listener) OVERRIDE; + virtual void AddKeyPressEventCallback( + const KeyPressEventCallback& callback) OVERRIDE; + virtual void RemoveKeyPressEventCallback( + const KeyPressEventCallback& callback) OVERRIDE; + virtual void AddMouseEventCallback( + const MouseEventCallback& callback) OVERRIDE; + virtual void RemoveMouseEventCallback( + const MouseEventCallback& callback) OVERRIDE; virtual void GetWebScreenInfo(WebKit::WebScreenInfo* result) OVERRIDE; virtual void GetSnapshotFromRenderer( const gfx::Rect& src_subrect, @@ -199,7 +204,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // 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(); + virtual void Init(); // Tells the renderer to die and then calls Destroy(). virtual void Shutdown(); @@ -267,9 +272,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, 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(); + // trigger a re-composite to the view. It may fail if a resize is pending, or + // if a composite has already been requested and not acked yet. + bool 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 @@ -335,7 +340,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // * when it receives a "commit" signal of GtkIMContext (on Linux); // * when insertText of NSTextInput is called (on Mac). void ImeConfirmComposition(const string16& text, - const ui::Range& replacement_range, + const gfx::Range& replacement_range, bool keep_selection); // Cancels an ongoing composition. @@ -365,7 +370,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, bool ShouldForwardTouchEvent() const; bool ShouldForwardGestureEvent( const GestureEventWithLatencyInfo& gesture_event) const; - bool HasQueuedGestureEvents() const; bool has_touch_handler() const { return has_touch_handler_; } @@ -463,6 +467,12 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, int renderer_host_id, const cc::CompositorFrameAck& ack); + // Called by the view to return resources to the compositor. + static void SendReclaimCompositorResources(int32 route_id, + uint32 output_surface_id, + int renderer_host_id, + const cc::CompositorFrameAck& ack); + // Called by the view in response to AcceleratedSurfaceBuffersSwapped for // platforms that support deferred GPU process descheduling. This does // nothing if the compositor thread is enabled. @@ -503,7 +513,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, return overscroll_controller_.get(); } - base::TimeDelta GetSyntheticScrollMessageInterval() const; + base::TimeDelta GetSyntheticGestureMessageInterval() const; // Sets whether the overscroll controller should be enabled for this page. void SetOverscrollControllerEnabled(bool enabled); @@ -648,15 +658,17 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, void OnUpdateIsDelayed(); void OnBeginSmoothScroll( const ViewHostMsg_BeginSmoothScroll_Params& params); + void OnBeginPinch( + const ViewHostMsg_BeginPinch_Params& params); virtual void OnFocus(); virtual void OnBlur(); void OnSetCursor(const WebCursor& cursor); void OnTextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode); + ui::TextInputMode input_mode, + bool can_compose_inline); #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) void OnImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds); #endif void OnImeCancelComposition(); @@ -729,6 +741,10 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, const TouchEventWithLatencyInfo& touch_event) OVERRIDE; virtual bool OnSendGestureEventImmediately( const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual void SetNeedsFlush() OVERRIDE; + virtual void DidFlush() OVERRIDE; + + // InputAckHandler virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, InputEventAckState ack_result) OVERRIDE; virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, @@ -737,7 +753,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, InputEventAckState ack_result) OVERRIDE; virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, InputEventAckState ack_result) OVERRIDE; - virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE; + virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE; void SimulateTouchGestureWithMouse(const WebKit::WebMouseEvent& mouse_event); @@ -745,7 +761,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // which may get in recursive loops). void DelayedAutoResized(); - // Our delegate, which wants to know mainly about keyboard events. // It will remain non-NULL until DetachDelegate() is called. RenderWidgetHostDelegate* delegate_; @@ -821,7 +836,10 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, AccessibilityMode accessibility_mode_; // Keyboard event listeners. - ObserverList<KeyboardListener> keyboard_listeners_; + std::vector<KeyPressEventCallback> key_press_event_callbacks_; + + // Mouse event callbacks. + std::vector<MouseEventCallback> mouse_event_callbacks_; // 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 @@ -899,7 +917,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_; - SmoothScrollGestureController smooth_scroll_gesture_controller_; + SyntheticGestureController synthetic_gesture_controller_; // Receives and handles all input events. scoped_ptr<InputRouter> input_router_; diff --git a/chromium/content/browser/renderer_host/render_widget_host_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_unittest.cc index 960ce5eb92a..21c368ab488 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/memory/shared_memory.h" #include "base/timer/timer.h" @@ -28,7 +29,7 @@ #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/screen.h" @@ -40,7 +41,7 @@ #if defined(OS_WIN) || defined(USE_AURA) #include "content/browser/renderer_host/ui_events_helper.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" #endif using base::TimeDelta; @@ -104,30 +105,6 @@ class TestOverscrollDelegate : public OverscrollControllerDelegate { DISALLOW_COPY_AND_ASSIGN(TestOverscrollDelegate); }; -// MockKeyboardListener -------------------------------------------------------- -class MockKeyboardListener : public KeyboardListener { - public: - MockKeyboardListener() - : handle_key_press_event_(false) { - } - virtual ~MockKeyboardListener() {} - - // KeyboardListener: - virtual bool HandleKeyPressEvent( - const NativeWebKeyboardEvent& event) OVERRIDE { - return handle_key_press_event_; - } - - void set_handle_key_press_event(bool handle) { - handle_key_press_event_ = handle; - } - - private: - bool handle_key_press_event_; - - DISALLOW_COPY_AND_ASSIGN(MockKeyboardListener); -}; - // MockInputRouter ------------------------------------------------------------- class MockInputRouter : public InputRouter { @@ -145,12 +122,11 @@ class MockInputRouter : public InputRouter { virtual ~MockInputRouter() {} // InputRouter - virtual bool SendInput(IPC::Message* message) OVERRIDE { + virtual void Flush() OVERRIDE { + flush_called_ = true; + } + virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE { send_event_called_ = true; - - // SendInput takes ownership of message - delete message; - return true; } virtual void SendMouseEvent( @@ -195,7 +171,6 @@ class MockInputRouter : public InputRouter { const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE { return true; } - virtual bool HasQueuedGestureEvents() const OVERRIDE { return true; } // IPC::Listener virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { @@ -203,6 +178,7 @@ class MockInputRouter : public InputRouter { return false; } + bool flush_called_; bool send_event_called_; bool sent_mouse_event_; bool sent_wheel_event_; @@ -225,7 +201,7 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl { RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id), + : RenderWidgetHostImpl(delegate, process, routing_id, false), unresponsive_timer_fired_(false) { immediate_input_router_ = static_cast<ImmediateInputRouter*>(input_router_.get()); @@ -628,11 +604,21 @@ class MockPaintingObserver : public NotificationObserver { class RenderWidgetHostTest : public testing::Test { public: - RenderWidgetHostTest() : process_(NULL) { + RenderWidgetHostTest() + : process_(NULL), + handle_key_press_event_(false), + handle_mouse_event_(false) { } virtual ~RenderWidgetHostTest() { } + bool KeyPressEventCallback(const NativeWebKeyboardEvent& /* event */) { + return handle_key_press_event_; + } + bool MouseEventCallback(const WebKit::WebMouseEvent& /* event */) { + return handle_mouse_event_; + } + protected: // testing::Test virtual void SetUp() { @@ -835,6 +821,8 @@ class RenderWidgetHostTest : public testing::Test { scoped_ptr<MockRenderWidgetHost> host_; scoped_ptr<TestView> view_; scoped_ptr<gfx::Screen> screen_; + bool handle_key_press_event_; + bool handle_mouse_event_; private: WebTouchEvent touch_event_; @@ -857,13 +845,12 @@ class RenderWidgetHostWithSourceTest // ----------------------------------------------------------------------------- TEST_F(RenderWidgetHostTest, Resize) { - // The initial bounds is the empty rect, but the screen info hasn't been sent - // yet, so setting it to the same thing should send the resize message. + // The initial bounds is the empty rect, and the screen info hasn't been sent + // yet, so setting it to the same thing shouldn't send the resize message. view_->set_bounds(gfx::Rect()); host_->WasResized(); EXPECT_FALSE(host_->resize_ack_pending_); - EXPECT_EQ(gfx::Size(), host_->last_requested_size_); - EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); + EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); // Setting the bounds to a "real" rect should send out the notification. // but should not expect ack for empty physical backing size. @@ -1397,6 +1384,7 @@ TEST_F(RenderWidgetHostTest, WheelScrollEventOverscrolls) { SimulateMouseMove(5, 10, 0); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, process_->sink().message_count()); } // Tests that if some scroll events are consumed towards the start, then @@ -1618,10 +1606,10 @@ TEST_F(RenderWidgetHostTest, ScrollEventsOverscrollWithZeroFling) { // Send a fling start, but with a small velocity, so that the overscroll is // aborted. The fling should proceed to the renderer, through the gesture // event filter. - SimulateGestureFlingStartEvent(0.f, 0.f, WebGestureEvent::Touchpad); + SimulateGestureFlingStartEvent(10.f, 0.f, WebGestureEvent::Touchpad); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); - EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); - EXPECT_EQ(0U, process_->sink().message_count()); + EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize()); + EXPECT_EQ(1U, process_->sink().message_count()); } // Tests that a fling in the opposite direction of the overscroll cancels the @@ -1646,11 +1634,13 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode()); + process_->sink().ClearMessages(); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, process_->sink().message_count()); SendInputEventACK(WebInputEvent::GestureScrollEnd, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); } @@ -1670,9 +1660,12 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->current_mode()); + process_->sink().ClearMessages(); + SimulateGestureFlingStartEvent(100, 0, WebGestureEvent::Touchscreen); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, process_->sink().message_count()); } } @@ -1750,11 +1743,14 @@ TEST_F(RenderWidgetHostTest, GestureScrollConsumedHorizontal) { INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + process_->sink().ClearMessages(); // Send another gesture event and ACK as not being processed. This should // not initiate overscroll because the beginning of the scroll event did - // scroll some content on the page. + // scroll some content on the page. Since there was no overscroll, the event + // should reach the renderer. SimulateGestureScrollUpdateEvent(55, 0, 0); + EXPECT_EQ(1U, process_->sink().message_count()); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); @@ -2301,12 +2297,15 @@ TEST_F(RenderWidgetHostTest, OverscrollMouseMoveCompletion) { // Overscroll gesture is in progress. Send a mouse-move now. This should // complete the gesture (because the amount overscrolled is above the - // threshold), and consume the event. + // threshold). SimulateMouseMove(5, 10, 0); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); - EXPECT_EQ(0U, process_->sink().message_count()); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::MouseMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen); @@ -2499,32 +2498,30 @@ TEST_F(RenderWidgetHostTest, IgnoreInputEvent) { TEST_F(RenderWidgetHostTest, KeyboardListenerIgnoresEvent) { host_->SetupForInputRouterTest(); - - scoped_ptr<MockKeyboardListener> keyboard_listener_(new MockKeyboardListener); - host_->AddKeyboardListener(keyboard_listener_.get()); - - keyboard_listener_->set_handle_key_press_event(false); + host_->AddKeyPressEventCallback( + base::Bind(&RenderWidgetHostTest::KeyPressEventCallback, + base::Unretained(this))); + handle_key_press_event_ = false; SimulateKeyboardEvent(WebInputEvent::RawKeyDown); EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); - - host_->RemoveKeyboardListener(keyboard_listener_.get()); } TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) { host_->SetupForInputRouterTest(); - scoped_ptr<MockKeyboardListener> keyboard_listener_(new MockKeyboardListener); - host_->AddKeyboardListener(keyboard_listener_.get()); + host_->AddKeyPressEventCallback( + base::Bind(&RenderWidgetHostTest::KeyPressEventCallback, + base::Unretained(this))); - // KeyboardListener handles the first event - keyboard_listener_->set_handle_key_press_event(true); + // The callback handles the first event + handle_key_press_event_ = true; SimulateKeyboardEvent(WebInputEvent::RawKeyDown); EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); // Following Char events should be suppressed - keyboard_listener_->set_handle_key_press_event(false); + handle_key_press_event_ = false; SimulateKeyboardEvent(WebInputEvent::Char); EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); SimulateKeyboardEvent(WebInputEvent::Char); @@ -2537,8 +2534,24 @@ TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) { host_->mock_input_router()->sent_keyboard_event_ = false; SimulateKeyboardEvent(WebInputEvent::Char); EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); +} + +TEST_F(RenderWidgetHostTest, MouseEventCallbackCanHandleEvent) { + host_->SetupForInputRouterTest(); + + host_->AddMouseEventCallback( + base::Bind(&RenderWidgetHostTest::MouseEventCallback, + base::Unretained(this))); + + handle_mouse_event_ = true; + SimulateMouseEvent(WebInputEvent::MouseDown); + + EXPECT_FALSE(host_->mock_input_router()->sent_mouse_event_); + + handle_mouse_event_ = false; + SimulateMouseEvent(WebInputEvent::MouseDown); - host_->RemoveKeyboardListener(keyboard_listener_.get()); + EXPECT_TRUE(host_->mock_input_router()->sent_mouse_event_); } TEST_F(RenderWidgetHostTest, InputRouterReceivesHandleInputEvent_ACK) { diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_android.cc b/chromium/content/browser/renderer_host/render_widget_host_view_android.cc index 8e885407e5c..3b94566748f 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_android.cc @@ -6,8 +6,9 @@ #include <android/bitmap.h> +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" @@ -20,6 +21,7 @@ #include "cc/output/compositor_frame_ack.h" #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" +#include "cc/resources/single_release_callback.h" #include "cc/trees/layer_tree_host.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/browser/android/content_view_core_impl.h" @@ -28,10 +30,10 @@ #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/compositor_impl_android.h" #include "content/browser/renderer_host/dip_util.h" +#include "content/browser/renderer_host/generic_touch_gesture_android.h" #include "content/browser/renderer_host/image_transport_factory_android.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/surface_texture_transport_client_android.h" -#include "content/browser/renderer_host/touch_smooth_scroll_gesture_android.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/gpu_messages.h" #include "content/common/input_messages.h" @@ -51,6 +53,7 @@ namespace content { namespace { const int kUndefinedOutputSurfaceId = -1; +const int kMinimumPointerDistance = 50; void InsertSyncPointAndAckForGpu( int gpu_host_id, int route_id, const std::string& return_mailbox) { @@ -88,12 +91,12 @@ void SendImeEventAck(RenderWidgetHostImpl* host) { void CopyFromCompositingSurfaceFinished( const base::Callback<void(bool, const SkBitmap&)>& callback, - const cc::TextureMailbox::ReleaseCallback& release_callback, + scoped_ptr<cc::SingleReleaseCallback> release_callback, scoped_ptr<SkBitmap> bitmap, scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, bool result) { bitmap_pixels_lock.reset(); - release_callback.Run(0, false); + release_callback->Run(0, false); callback.Run(result, *bitmap); } @@ -196,7 +199,7 @@ RenderWidgetHostViewAndroid::GetRenderWidgetHost() const { } void RenderWidgetHostViewAndroid::WasShown() { - if (!host_->is_hidden()) + if (!host_ || !host_->is_hidden()) return; host_->WasShown(); @@ -205,7 +208,7 @@ void RenderWidgetHostViewAndroid::WasShown() { void RenderWidgetHostViewAndroid::WasHidden() { RunAckCallbacks(); - if (host_->is_hidden()) + if (!host_ || host_->is_hidden()) return; // Inform the renderer that we are being hidden so it can reduce its resource @@ -353,6 +356,8 @@ void RenderWidgetHostViewAndroid::Show() { are_layers_attached_ = true; AttachLayers(); + + WasShown(); } void RenderWidgetHostViewAndroid::Hide() { @@ -361,6 +366,8 @@ void RenderWidgetHostViewAndroid::Hide() { are_layers_attached_ = false; RemoveLayers(); + + WasHidden(); } bool RenderWidgetHostViewAndroid::IsShowing() { @@ -406,8 +413,8 @@ void RenderWidgetHostViewAndroid::SetIsLoading(bool is_loading) { void RenderWidgetHostViewAndroid::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { // Unused on Android, which uses OnTextInputChanged instead. } @@ -420,9 +427,9 @@ void RenderWidgetHostViewAndroid::OnTextInputStateChanged( // If an acknowledgement is required for this event, regardless of how we exit // from this method, we must acknowledge that we processed the input state // change. - base::ScopedClosureRunner ack_caller(base::Bind(&SendImeEventAck, host_)); - if (!params.require_ack) - ack_caller.Release(); + base::ScopedClosureRunner ack_caller; + if (params.require_ack) + ack_caller.Reset(base::Bind(&SendImeEventAck, host_)); if (!IsShowing()) return; @@ -510,7 +517,7 @@ void RenderWidgetHostViewAndroid::SetTooltipText( void RenderWidgetHostViewAndroid::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { RenderWidgetHostViewBase::SelectionChanged(text, offset, range); if (text.empty() || range.is_empty() || !content_view_core_) @@ -606,14 +613,28 @@ void RenderWidgetHostViewAndroid::ShowDisambiguationPopup( content_view_core_->ShowDisambiguationPopup(target_rect, zoomed_bitmap); } -SmoothScrollGesture* RenderWidgetHostViewAndroid::CreateSmoothScrollGesture( +SyntheticGesture* RenderWidgetHostViewAndroid::CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) { - return new TouchSmoothScrollGestureAndroid( - pixels_to_scroll, + return new GenericTouchGestureAndroid( + GetRenderWidgetHost(), + content_view_core_->CreateOnePointTouchGesture( + mouse_event_x, mouse_event_y, + 0, scroll_down ? -pixels_to_scroll : pixels_to_scroll)); +} + +SyntheticGesture* RenderWidgetHostViewAndroid::CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) { + int distance_between_pointers = zoom_in ? + kMinimumPointerDistance : (kMinimumPointerDistance + pixels_to_move); + return new GenericTouchGestureAndroid( GetRenderWidgetHost(), - content_view_core_->CreateSmoothScroller( - scroll_down, mouse_event_x, mouse_event_y)); + content_view_core_->CreateTwoPointTouchGesture( + anchor_x, anchor_y - distance_between_pointers / 2, + 0, (zoom_in ? -pixels_to_move : pixels_to_move) / 2, + anchor_x, anchor_y + distance_between_pointers / 2, + 0, (zoom_in ? pixels_to_move : -pixels_to_move) / 2)); } void RenderWidgetHostViewAndroid::OnAcceleratedCompositingStateChange() { @@ -828,7 +849,7 @@ void RenderWidgetHostViewAndroid::CreateOverscrollEffectIfNecessary() { if (!overscroll_effect_enabled_ || overscroll_effect_) return; - overscroll_effect_ = OverscrollGlow::Create(true); + overscroll_effect_ = OverscrollGlow::Create(true, content_size_in_layer_); // Prevent future creation attempts on failure. if (!overscroll_effect_) @@ -950,8 +971,8 @@ InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent( return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; } -void RenderWidgetHostViewAndroid::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewAndroid::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (!host_ || host_->accessibility_mode() != AccessibilityModeComplete || !content_view_core_) { @@ -965,7 +986,7 @@ void RenderWidgetHostViewAndroid::OnAccessibilityNotifications( BrowserAccessibilityManagerAndroid::GetEmptyDocument(), this)); } - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } void RenderWidgetHostViewAndroid::SetAccessibilityFocus(int acc_obj_id) { @@ -1183,6 +1204,7 @@ WebKit::WebGraphicsContext3D* RenderWidgetHostViewAndroid::Context3d() { bool RenderWidgetHostViewAndroid::PrepareTextureMailbox( cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* release_callback, bool use_shared_memory) { return false; } @@ -1223,23 +1245,25 @@ void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult( new SkAutoLockPixels(*bitmap)); uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); - scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture(); - DCHECK(texture_mailbox->IsTexture()); - if (!texture_mailbox->IsTexture()) + cc::TextureMailbox texture_mailbox; + scoped_ptr<cc::SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox->name(), - texture_mailbox->sync_point(), + texture_mailbox.name(), + texture_mailbox.sync_point(), result->size(), gfx::Rect(result->size()), dst_size_in_pixel, pixels, base::Bind(&CopyFromCompositingSurfaceFinished, callback, - texture_mailbox->callback(), + base::Passed(&release_callback), base::Passed(&bitmap), base::Passed(&bitmap_pixels_lock))); } @@ -1264,7 +1288,7 @@ void RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult( DCHECK_EQ(source->width(), dst_size_in_pixel.width()); DCHECK_EQ(source->height(), dst_size_in_pixel.height()); - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); callback.Run(true, *source); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_android.h b/chromium/content/browser/renderer_host/render_widget_host_view_android.h index d8ea83da7ce..6f248076e97 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_android.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_android.h @@ -36,6 +36,7 @@ namespace cc { class CopyOutputResult; class DelegatedRendererLayer; class Layer; +class SingleReleaseCallback; class TextureLayer; } @@ -98,8 +99,8 @@ class RenderWidgetHostViewAndroid virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -112,7 +113,7 @@ class RenderWidgetHostViewAndroid virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -152,8 +153,8 @@ class RenderWidgetHostViewAndroid const WebKit::WebInputEvent& input_event) OVERRIDE; virtual void GestureEventAck(int gesture_event_type, InputEventAckState ack_result) OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; @@ -165,9 +166,12 @@ class RenderWidgetHostViewAndroid gfx::Vector2dF current_fling_velocity) OVERRIDE; virtual void ShowDisambiguationPopup(const gfx::Rect& target_rect, const SkBitmap& zoomed_bitmap) OVERRIDE; - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) OVERRIDE; + virtual SyntheticGesture* CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) OVERRIDE; // Implementation of BrowserAccessibilityDelegate: virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE; @@ -184,8 +188,10 @@ class RenderWidgetHostViewAndroid // cc::TextureLayerClient implementation. virtual unsigned PrepareTexture() OVERRIDE; virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE; - virtual bool PrepareTextureMailbox(cc::TextureMailbox* mailbox, - bool use_shared_memory) OVERRIDE; + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE; // cc::DelegatedRendererLayerClient implementation. virtual void DidCommitFrameData() OVERRIDE; diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc index 33ee9524e73..2eb34b98d38 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -4,8 +4,9 @@ #include "content/browser/renderer_host/render_widget_host_view_aura.h" +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/logging.h" @@ -16,8 +17,11 @@ #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" #include "cc/resources/texture_mailbox.h" +#include "cc/trees/layer_tree_settings.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/aura/compositor_resize_lock.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/renderer_host/backing_store_aura.h" #include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/overscroll_controller.h" @@ -57,13 +61,13 @@ #include "ui/aura/window_observer.h" #include "ui/aura/window_tracker.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/gestures/gesture_recognizer.h" #include "ui/base/hit_test.h" #include "ui/base/ime/input_method.h" #include "ui/base/ui_base_types.h" #include "ui/compositor/layer.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/canvas.h" #include "ui/gfx/display.h" #include "ui/gfx/rect_conversions.h" @@ -77,6 +81,7 @@ #include "content/browser/accessibility/browser_accessibility_win.h" #include "ui/base/win/hidden_window.h" #include "ui/gfx/gdi_util.h" +#include "ui/gfx/win/dpi.h" #endif using gfx::RectToSkIRect; @@ -100,10 +105,10 @@ class MemoryHolder : public base::RefCounted<MemoryHolder> { frame_size_(frame_size), callback_(callback) {} - cc::TextureMailbox GetMailbox() { - return cc::TextureMailbox( - shared_memory_.get(), - frame_size_, + void GetMailbox(cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* release_callback) { + *mailbox = cc::TextureMailbox(shared_memory_.get(), frame_size_); + *release_callback = cc::SingleReleaseCallback::Create( base::Bind(ReleaseMailbox, make_scoped_refptr(this))); } @@ -118,6 +123,11 @@ class MemoryHolder : public base::RefCounted<MemoryHolder> { namespace { +void MailboxReleaseCallback(scoped_ptr<base::SharedMemory> shared_memory, + unsigned sync_point, bool lost_resource) { + // NOTE: shared_memory will get released when we go out of scope. +} + // In mouse lock mode, we need to prevent the (invisible) cursor from hitting // the border of the view, in order to get valid movement information. However, // forcing the cursor back to the center of the view after each mouse move @@ -567,73 +577,6 @@ class RenderWidgetHostViewAura::TransientWindowObserver #endif -class RenderWidgetHostViewAura::ResizeLock { - public: - ResizeLock(aura::RootWindow* root_window, - const gfx::Size new_size, - bool defer_compositor_lock) - : root_window_(root_window), - new_size_(new_size), - compositor_lock_(defer_compositor_lock ? - NULL : - root_window_->compositor()->GetCompositorLock()), - weak_ptr_factory_(this), - defer_compositor_lock_(defer_compositor_lock) { - TRACE_EVENT_ASYNC_BEGIN2("ui", "ResizeLock", this, - "width", new_size_.width(), - "height", new_size_.height()); - root_window_->HoldPointerMoves(); - - BrowserThread::PostDelayedTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&RenderWidgetHostViewAura::ResizeLock::CancelLock, - weak_ptr_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs)); - } - - ~ResizeLock() { - CancelLock(); - TRACE_EVENT_ASYNC_END2("ui", "ResizeLock", this, - "width", new_size_.width(), - "height", new_size_.height()); - } - - void UnlockCompositor() { - defer_compositor_lock_ = false; - compositor_lock_ = NULL; - } - - void CancelLock() { - if (!root_window_) - return; - UnlockCompositor(); - root_window_->ReleasePointerMoves(); - root_window_ = NULL; - } - - const gfx::Size& expected_size() const { - return new_size_; - } - - bool GrabDeferredLock() { - if (root_window_ && defer_compositor_lock_) { - compositor_lock_ = root_window_->compositor()->GetCompositorLock(); - defer_compositor_lock_ = false; - return true; - } - return false; - } - - private: - aura::RootWindow* root_window_; - gfx::Size new_size_; - scoped_refptr<ui::CompositorLock> compositor_lock_; - base::WeakPtrFactory<ResizeLock> weak_ptr_factory_; - bool defer_compositor_lock_; - - DISALLOW_COPY_AND_ASSIGN(ResizeLock); -}; - //////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewAura, public: @@ -646,8 +589,11 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host) popup_child_host_view_(NULL), is_loading_(false), text_input_type_(ui::TEXT_INPUT_TYPE_NONE), + text_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT), can_compose_inline_(true), has_composition_text_(false), + last_output_surface_id_(0), + skipped_frames_(false), last_swapped_surface_scale_factor_(1.f), paint_canvas_(NULL), synthetic_move_sent_(false), @@ -746,18 +692,26 @@ RenderWidgetHost* RenderWidgetHostViewAura::GetRenderWidgetHost() const { } void RenderWidgetHostViewAura::WasShown() { + DCHECK(host_); if (!host_->is_hidden()) return; host_->WasShown(); + if (framebuffer_holder_) + FrameMemoryManager::GetInstance()->SetFrameVisibility(this, true); - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(window_->GetRootWindow()); - if (cursor_client) - NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible()); + aura::RootWindow* root = window_->GetRootWindow(); + if (root) { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(root); + if (cursor_client) + NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible()); + } if (!current_surface_.get() && host_->is_accelerated_compositing_active() && !released_front_lock_.get()) { - released_front_lock_ = GetCompositor()->GetCompositorLock(); + ui::Compositor* compositor = GetCompositor(); + if (compositor) + released_front_lock_ = compositor->GetCompositorLock(); } #if defined(OS_WIN) @@ -768,9 +722,11 @@ void RenderWidgetHostViewAura::WasShown() { } void RenderWidgetHostViewAura::WasHidden() { - if (host_->is_hidden()) + if (!host_ || host_->is_hidden()) return; host_->WasHidden(); + if (framebuffer_holder_) + FrameMemoryManager::GetInstance()->SetFrameVisibility(this, false); released_front_lock_ = NULL; @@ -803,39 +759,69 @@ void RenderWidgetHostViewAura::SetBounds(const gfx::Rect& rect) { } void RenderWidgetHostViewAura::MaybeCreateResizeLock() { - gfx::Size desired_size = window_->bounds().size(); - if (!host_->should_auto_resize() && - !resize_lock_.get() && - desired_size != current_frame_size_ && - host_->is_accelerated_compositing_active()) { - aura::RootWindow* root_window = window_->GetRootWindow(); - ui::Compositor* compositor = root_window ? - root_window->compositor() : NULL; - if (root_window && compositor) { - // Listen to changes in the compositor lock state. - if (!compositor->HasObserver(this)) - compositor->AddObserver(this); - -// On Windows while resizing, the the resize locks makes us mis-paint a white -// vertical strip (including the non-client area) if the content composition is -// lagging the UI composition. So here we disable the throttling so that the UI -// bits can draw ahead of the content thereby reducing the amount of whiteout. -// Because this causes the content to be drawn at wrong sizes while resizing -// we compensate by blocking the UI thread in Compositor::Draw() by issuing a -// FinishAllRendering() if we are resizing. -#if !defined (OS_WIN) - bool defer_compositor_lock = - can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || - can_lock_compositor_ == NO_PENDING_COMMIT; - - if (can_lock_compositor_ == YES) - can_lock_compositor_ = YES_DID_LOCK; - - resize_lock_.reset(new ResizeLock(root_window, desired_size, - defer_compositor_lock)); + if (!ShouldCreateResizeLock()) + return; + DCHECK(window_->GetRootWindow()); + DCHECK(window_->GetRootWindow()->compositor()); + + // Listen to changes in the compositor lock state. + ui::Compositor* compositor = window_->GetRootWindow()->compositor(); + if (!compositor->HasObserver(this)) + compositor->AddObserver(this); + + bool defer_compositor_lock = + can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || + can_lock_compositor_ == NO_PENDING_COMMIT; + + if (can_lock_compositor_ == YES) + can_lock_compositor_ = YES_DID_LOCK; + + resize_lock_ = CreateResizeLock(defer_compositor_lock); +} + +bool RenderWidgetHostViewAura::ShouldCreateResizeLock() { + // On Windows while resizing, the the resize locks makes us mis-paint a white + // vertical strip (including the non-client area) if the content composition + // is lagging the UI composition. So here we disable the throttling so that + // the UI bits can draw ahead of the content thereby reducing the amount of + // whiteout. Because this causes the content to be drawn at wrong sizes while + // resizing we compensate by blocking the UI thread in Compositor::Draw() by + // issuing a FinishAllRendering() if we are resizing. +#if defined (OS_WIN) + return false; #endif - } - } + + if (resize_lock_) + return false; + + if (host_->should_auto_resize()) + return false; + if (!host_->is_accelerated_compositing_active()) + return false; + + gfx::Size desired_size = window_->bounds().size(); + if (desired_size == current_frame_size_) + return false; + + aura::RootWindow* root_window = window_->GetRootWindow(); + if (!root_window) + return false; + + ui::Compositor* compositor = root_window->compositor(); + if (!compositor) + return false; + + return true; +} + +scoped_ptr<ResizeLock> RenderWidgetHostViewAura::CreateResizeLock( + bool defer_compositor_lock) { + gfx::Size desired_size = window_->bounds().size(); + return scoped_ptr<ResizeLock>(new CompositorResizeLock( + window_->GetRootWindow(), + desired_size, + defer_compositor_lock, + base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs))); } gfx::NativeView RenderWidgetHostViewAura::GetNativeView() const { @@ -983,10 +969,12 @@ bool RenderWidgetHostViewAura::IsSurfaceAvailableForCopy() const { void RenderWidgetHostViewAura::Show() { window_->Show(); + WasShown(); } void RenderWidgetHostViewAura::Hide() { window_->Hide(); + WasHidden(); } bool RenderWidgetHostViewAura::IsShowing() { @@ -1010,18 +998,6 @@ void RenderWidgetHostViewAura::SetBackground(const SkBitmap& background) { window_->layer()->SetFillsBoundsOpaquely(background.isOpaque()); } -#if defined(OS_WIN) -gfx::NativeViewAccessible -RenderWidgetHostViewAura::AccessibleObjectFromChildId(long child_id) { - BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager(); - if (!manager) - return NULL; - - return manager->ToBrowserAccessibilityManagerWin()->GetFromUniqueIdWin( - child_id); -} -#endif // defined(OS_WIN) - void RenderWidgetHostViewAura::UpdateCursor(const WebCursor& cursor) { current_cursor_ = cursor; const gfx::Display display = gfx::Screen::GetScreenFor(window_)-> @@ -1039,11 +1015,13 @@ void RenderWidgetHostViewAura::SetIsLoading(bool is_loading) { void RenderWidgetHostViewAura::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (text_input_type_ != type || + text_input_mode_ != input_mode || can_compose_inline_ != can_compose_inline) { text_input_type_ = type; + text_input_mode_ = input_mode; can_compose_inline_ = can_compose_inline; if (GetInputMethod()) GetInputMethod()->OnTextInputTypeChanged(this); @@ -1059,7 +1037,7 @@ void RenderWidgetHostViewAura::ImeCancelComposition() { } void RenderWidgetHostViewAura::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { composition_character_bounds_ = character_bounds; } @@ -1144,7 +1122,7 @@ void RenderWidgetHostViewAura::SetTooltipText(const string16& tooltip_text) { void RenderWidgetHostViewAura::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { RenderWidgetHostViewBase::SelectionChanged(text, offset, range); #if defined(USE_X11) && !defined(OS_CHROMEOS) @@ -1308,9 +1286,14 @@ void RenderWidgetHostViewAura::UpdateExternalTexture() { current_frame_size_ = ConvertSizeToDIP( current_surface_->device_scale_factor(), current_surface_->size()); CheckResizeLock(); + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); } else if (is_compositing_active && framebuffer_holder_) { - cc::TextureMailbox mailbox = framebuffer_holder_->GetMailbox(); + cc::TextureMailbox mailbox; + scoped_ptr<cc::SingleReleaseCallback> callback; + framebuffer_holder_->GetMailbox(&mailbox, &callback); window_->layer()->SetTextureMailbox(mailbox, + callback.Pass(), last_swapped_surface_scale_factor_); current_frame_size_ = ConvertSizeToDIP(last_swapped_surface_scale_factor_, mailbox.shared_memory_size()); @@ -1319,6 +1302,8 @@ void RenderWidgetHostViewAura::UpdateExternalTexture() { window_->layer()->SetExternalTexture(NULL); resize_lock_.reset(); host_->WasResized(); + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); } } @@ -1440,20 +1425,58 @@ void RenderWidgetHostViewAura::SwapDelegatedFrame( scoped_ptr<cc::DelegatedFrameData> frame_data, float frame_device_scale_factor, const ui::LatencyInfo& latency_info) { + gfx::Size frame_size; gfx::Size frame_size_in_dip; + gfx::Rect damage_rect; + gfx::Rect damage_rect_in_dip; + if (!frame_data->render_pass_list.empty()) { - frame_size_in_dip = gfx::ToFlooredSize(gfx::ScaleSize( - frame_data->render_pass_list.back()->output_rect.size(), - 1.f/frame_device_scale_factor)); + cc::RenderPass* root_pass = frame_data->render_pass_list.back(); + + frame_size = root_pass->output_rect.size(); + frame_size_in_dip = ConvertSizeToDIP(frame_device_scale_factor, frame_size); + + damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect); + damage_rect.Intersect(gfx::Rect(frame_size)); + damage_rect_in_dip = ConvertRectToDIP(frame_device_scale_factor, + damage_rect); } + + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); + if (ShouldSkipFrame(frame_size_in_dip)) { cc::CompositorFrameAck ack; - ack.resources.swap(frame_data->resource_list); + cc::TransferableResource::ReturnResources(frame_data->resource_list, + &ack.resources); RenderWidgetHostImpl::SendSwapCompositorFrameAck( host_->GetRoutingID(), output_surface_id, host_->GetProcess()->GetID(), ack); + skipped_frames_ = true; return; } + + if (skipped_frames_) { + skipped_frames_ = false; + damage_rect = gfx::Rect(frame_size); + damage_rect_in_dip = gfx::Rect(frame_size_in_dip); + + // Give the same damage rect to the compositor. + cc::RenderPass* root_pass = frame_data->render_pass_list.back(); + root_pass->damage_rect = damage_rect; + } + + if (output_surface_id != last_output_surface_id_) { + // Resource ids are scoped by the output surface. + // If the originating output surface doesn't match the last one, it + // indicates the renderer's output surface may have been recreated, in which + // case we should recreate the DelegatedRendererLayer, to avoid matching + // resources from the old one with resources from the new one which would + // have the same id. + window_->layer()->SetDelegatedFrame(scoped_ptr<cc::DelegatedFrameData>(), + frame_size_in_dip); + last_output_surface_id_ = output_surface_id; + } window_->layer()->SetDelegatedFrame(frame_data.Pass(), frame_size_in_dip); released_front_lock_ = NULL; current_frame_size_ = frame_size_in_dip; @@ -1461,6 +1484,7 @@ void RenderWidgetHostViewAura::SwapDelegatedFrame( if (paint_observer_) paint_observer_->OnUpdateCompositorContent(); + window_->SchedulePaintInRect(damage_rect_in_dip); ui::Compositor* compositor = GetCompositor(); if (!compositor) { @@ -1493,7 +1517,8 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( gfx::Size frame_size_in_dip = ConvertSizeToDIP(frame_device_scale_factor, frame_size); if (ShouldSkipFrame(frame_size_in_dip)) { - SendSoftwareFrameAck(output_surface_id, frame_data->id); + ReleaseSoftwareFrame(output_surface_id, frame_data->id); + SendSoftwareFrameAck(output_surface_id); return; } @@ -1522,41 +1547,83 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( scoped_refptr<MemoryHolder> holder(new MemoryHolder( shared_memory.Pass(), frame_size, - base::Bind(&RenderWidgetHostViewAura::SendSoftwareFrameAck, + base::Bind(&RenderWidgetHostViewAura::ReleaseSoftwareFrame, AsWeakPtr(), output_surface_id, frame_data->id))); framebuffer_holder_.swap(holder); - cc::TextureMailbox mailbox = framebuffer_holder_->GetMailbox(); + cc::TextureMailbox mailbox; + scoped_ptr<cc::SingleReleaseCallback> callback; + framebuffer_holder_->GetMailbox(&mailbox, &callback); DCHECK(mailbox.IsSharedMemory()); current_frame_size_ = frame_size_in_dip; released_front_lock_ = NULL; CheckResizeLock(); - window_->layer()->SetTextureMailbox(mailbox, frame_device_scale_factor); + window_->layer()->SetTextureMailbox(mailbox, + callback.Pass(), + frame_device_scale_factor); window_->SchedulePaintInRect( ConvertRectToDIP(frame_device_scale_factor, damage_rect)); ui::Compositor* compositor = GetCompositor(); - if (compositor) + if (compositor) { compositor->SetLatencyInfo(latency_info); + AddOnCommitCallbackAndDisableLocks( + base::Bind(&RenderWidgetHostViewAura::SendSoftwareFrameAck, + AsWeakPtr(), + output_surface_id)); + } if (paint_observer_) paint_observer_->OnUpdateCompositorContent(); DidReceiveFrameFromRenderer(); + FrameMemoryManager::GetInstance()->AddFrame(this, !host_->is_hidden()); } -void RenderWidgetHostViewAura::SendSoftwareFrameAck( - uint32 output_surface_id, unsigned software_frame_id) { +void RenderWidgetHostViewAura::SendSoftwareFrameAck(uint32 output_surface_id) { + unsigned software_frame_id = 0; + if (!released_software_frames_.empty()) { + unsigned released_output_surface_id = + released_software_frames_.back().output_surface_id; + if (released_output_surface_id == output_surface_id) { + software_frame_id = released_software_frames_.back().frame_id; + released_software_frames_.pop_back(); + } + } + cc::CompositorFrameAck ack; ack.last_software_frame_id = software_frame_id; RenderWidgetHostImpl::SendSwapCompositorFrameAck( host_->GetRoutingID(), output_surface_id, host_->GetProcess()->GetID(), ack); + SendReclaimSoftwareFrames(); +} + +void RenderWidgetHostViewAura::SendReclaimSoftwareFrames() { + while (!released_software_frames_.empty()) { + cc::CompositorFrameAck ack; + ack.last_software_frame_id = released_software_frames_.back().frame_id; + RenderWidgetHostImpl::SendReclaimCompositorResources( + host_->GetRoutingID(), + released_software_frames_.back().output_surface_id, + host_->GetProcess()->GetID(), + ack); + released_software_frames_.pop_back(); + } +} + +void RenderWidgetHostViewAura::ReleaseSoftwareFrame( + uint32 output_surface_id, + unsigned software_frame_id) { + SendReclaimSoftwareFrames(); + released_software_frames_.push_back( + ReleasedFrameInfo(output_surface_id, software_frame_id)); } void RenderWidgetHostViewAura::OnSwapCompositorFrame( uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) { + TRACE_EVENT0("content", "RenderWidgetHostViewAura::OnSwapCompositorFrame"); if (frame->delegated_frame_data) { SwapDelegatedFrame(output_surface_id, frame->delegated_frame_data.Pass(), @@ -1620,6 +1687,8 @@ void RenderWidgetHostViewAura::BuffersSwapped( const BufferPresentedCallback& ack_callback) { scoped_refptr<ui::Texture> previous_texture(current_surface_); const gfx::Rect surface_rect = gfx::Rect(surface_size); + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); if (!SwapBuffersPrepare(surface_rect, surface_scale_factor, @@ -1724,7 +1793,7 @@ void RenderWidgetHostViewAura::AcceleratedSurfaceRelease() { } bool RenderWidgetHostViewAura::HasAcceleratedSurface( - const gfx::Size& desired_size) { + const gfx::Size& desired_size) { // Aura doesn't use GetBackingStore for accelerated pages, so it doesn't // matter what is returned here as GetBackingStore is the only caller of this // method. TODO(jbates) implement this if other Aura code needs it. @@ -1756,12 +1825,12 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResult( static void CopyFromCompositingSurfaceFinished( const base::Callback<void(bool, const SkBitmap&)>& callback, - const cc::TextureMailbox::ReleaseCallback& release_callback, + scoped_ptr<cc::SingleReleaseCallback> release_callback, scoped_ptr<SkBitmap> bitmap, scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, bool result) { bitmap_pixels_lock.reset(); - release_callback.Run(0, false); + release_callback->Run(0, false); callback.Run(result, *bitmap); } @@ -1793,23 +1862,25 @@ void RenderWidgetHostViewAura::PrepareTextureCopyOutputResult( new SkAutoLockPixels(*bitmap)); uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); - scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture(); - DCHECK(texture_mailbox->IsTexture()); - if (!texture_mailbox->IsTexture()) + cc::TextureMailbox texture_mailbox; + scoped_ptr<cc::SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox->name(), - texture_mailbox->sync_point(), + texture_mailbox.name(), + texture_mailbox.sync_point(), result->size(), gfx::Rect(result->size()), dst_size_in_pixel, pixels, base::Bind(&CopyFromCompositingSurfaceFinished, callback, - texture_mailbox->callback(), + base::Passed(&release_callback), base::Passed(&bitmap), base::Passed(&bitmap_pixels_lock))); } @@ -1831,7 +1902,7 @@ void RenderWidgetHostViewAura::PrepareBitmapCopyOutputResult( if (!source) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); SkBitmap bitmap = skia::ImageOperations::Resize( *source, @@ -1843,9 +1914,9 @@ void RenderWidgetHostViewAura::PrepareBitmapCopyOutputResult( static void CopyFromCompositingSurfaceFinishedForVideo( const base::Callback<void(bool)>& callback, - const cc::TextureMailbox::ReleaseCallback& release_callback, + scoped_ptr<cc::SingleReleaseCallback> release_callback, bool result) { - release_callback.Run(0, false); + release_callback->Run(0, false); callback.Run(result); } @@ -1908,7 +1979,7 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo( region_in_frame, video_frame.get()); } - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); callback.Run(true); return; } @@ -1918,9 +1989,11 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo( if (!gl_helper) return; - scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture(); - DCHECK(texture_mailbox->IsTexture()); - if (!texture_mailbox->IsTexture()) + cc::TextureMailbox texture_mailbox; + scoped_ptr<cc::SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) return; gfx::Rect result_rect(result->size()); @@ -1958,14 +2031,14 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo( yuv_readback_pipeline = rwhva->yuv_readback_pipeline_.get(); } - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); base::Callback<void(bool result)> finished_callback = base::Bind( &CopyFromCompositingSurfaceFinishedForVideo, callback, - texture_mailbox->callback()); + base::Passed(&release_callback)); yuv_readback_pipeline->ReadbackYUV( - texture_mailbox->name(), - texture_mailbox->sync_point(), + texture_mailbox.name(), + texture_mailbox.sync_point(), video_frame.get(), finished_callback); } @@ -1975,7 +2048,32 @@ void RenderWidgetHostViewAura::GetScreenInfo(WebScreenInfo* results) { } gfx::Rect RenderWidgetHostViewAura::GetBoundsInRootWindow() { +#if defined(OS_WIN) + // aura::Window::GetBoundsInScreen doesn't take non-client area into + // account. + RECT window_rect = {0}; + + aura::Window* top_level = window_->GetToplevelWindow(); + aura::RootWindow* root_window = top_level->GetRootWindow(); + if (!root_window) + return top_level->GetBoundsInScreen(); + HWND hwnd = root_window->GetAcceleratedWidget(); + ::GetWindowRect(hwnd, &window_rect); + gfx::Rect rect(window_rect); + + // Maximized windows are outdented from the work area by the frame thickness + // even though this "frame" is not painted. This confuses code (and people) + // that think of a maximized window as corresponding exactly to the work area. + // Correct for this by subtracting the frame thickness back off. + if (::IsZoomed(hwnd)) { + rect.Inset(GetSystemMetrics(SM_CXSIZEFRAME), + GetSystemMetrics(SM_CYSIZEFRAME)); + } + + return gfx::win::ScreenToDIPRect(rect); +#else return window_->GetToplevelWindow()->GetBoundsInScreen(); +#endif } void RenderWidgetHostViewAura::GestureEventAck(int gesture_event_type, @@ -2004,7 +2102,7 @@ void RenderWidgetHostViewAura::ProcessAckedTouchEvent( } } -SmoothScrollGesture* RenderWidgetHostViewAura::CreateSmoothScrollGesture( +SyntheticGesture* RenderWidgetHostViewAura::CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, @@ -2026,12 +2124,12 @@ void RenderWidgetHostViewAura::SetScrollOffsetPinning( // Not needed. Mac-only. } -void RenderWidgetHostViewAura::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewAura::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { BrowserAccessibilityManager* manager = GetOrCreateBrowserAccessibilityManager(); if (manager) - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); } gfx::GLSurfaceHandle RenderWidgetHostViewAura::GetCompositingSurface() { @@ -2122,7 +2220,7 @@ void RenderWidgetHostViewAura::SetCompositionText( void RenderWidgetHostViewAura::ConfirmCompositionText() { if (host_ && has_composition_text_) - host_->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), false); + host_->ImeConfirmComposition(string16(), gfx::Range::InvalidRange(), false); has_composition_text_ = false; } @@ -2135,7 +2233,7 @@ void RenderWidgetHostViewAura::ClearCompositionText() { void RenderWidgetHostViewAura::InsertText(const string16& text) { DCHECK(text_input_type_ != ui::TEXT_INPUT_TYPE_NONE); if (host_) - host_->ImeConfirmComposition(text, ui::Range::InvalidRange(), false); + host_->ImeConfirmComposition(text, gfx::Range::InvalidRange(), false); has_composition_text_ = false; } @@ -2166,7 +2264,7 @@ ui::TextInputType RenderWidgetHostViewAura::GetTextInputType() const { } ui::TextInputMode RenderWidgetHostViewAura::GetTextInputMode() const { - return ui::TEXT_INPUT_MODE_DEFAULT; + return text_input_mode_; } bool RenderWidgetHostViewAura::CanComposeInline() const { @@ -2178,18 +2276,18 @@ gfx::Rect RenderWidgetHostViewAura::ConvertRectToScreen(const gfx::Rect& rect) { gfx::Point end = gfx::Point(rect.right(), rect.bottom()); aura::RootWindow* root_window = window_->GetRootWindow(); - if (root_window) { - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(root_window); - screen_position_client->ConvertPointToScreen(window_, &origin); - screen_position_client->ConvertPointToScreen(window_, &end); - return gfx::Rect(origin.x(), - origin.y(), - end.x() - origin.x(), - end.y() - origin.y()); - } - - return rect; + if (!root_window) + return rect; + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(root_window); + if (!screen_position_client) + return rect; + screen_position_client->ConvertPointToScreen(window_, &origin); + screen_position_client->ConvertPointToScreen(window_, &end); + return gfx::Rect(origin.x(), + origin.y(), + end.x() - origin.x(), + end.y() - origin.y()); } gfx::Rect RenderWidgetHostViewAura::ConvertRectFromScreen( @@ -2231,40 +2329,40 @@ bool RenderWidgetHostViewAura::HasCompositionText() { return has_composition_text_; } -bool RenderWidgetHostViewAura::GetTextRange(ui::Range* range) { +bool RenderWidgetHostViewAura::GetTextRange(gfx::Range* range) { range->set_start(selection_text_offset_); range->set_end(selection_text_offset_ + selection_text_.length()); return true; } -bool RenderWidgetHostViewAura::GetCompositionTextRange(ui::Range* range) { +bool RenderWidgetHostViewAura::GetCompositionTextRange(gfx::Range* range) { // TODO(suzhe): implement this method when fixing http://crbug.com/55130. NOTIMPLEMENTED(); return false; } -bool RenderWidgetHostViewAura::GetSelectionRange(ui::Range* range) { +bool RenderWidgetHostViewAura::GetSelectionRange(gfx::Range* range) { range->set_start(selection_range_.start()); range->set_end(selection_range_.end()); return true; } -bool RenderWidgetHostViewAura::SetSelectionRange(const ui::Range& range) { +bool RenderWidgetHostViewAura::SetSelectionRange(const gfx::Range& range) { // TODO(suzhe): implement this method when fixing http://crbug.com/55130. NOTIMPLEMENTED(); return false; } -bool RenderWidgetHostViewAura::DeleteRange(const ui::Range& range) { +bool RenderWidgetHostViewAura::DeleteRange(const gfx::Range& range) { // TODO(suzhe): implement this method when fixing http://crbug.com/55130. NOTIMPLEMENTED(); return false; } bool RenderWidgetHostViewAura::GetTextFromRange( - const ui::Range& range, + const gfx::Range& range, string16* text) { - ui::Range selection_text_range(selection_text_offset_, + gfx::Range selection_text_range(selection_text_offset_, selection_text_offset_ + selection_text_.length()); if (!selection_text_range.Contains(range)) { @@ -2446,6 +2544,13 @@ void RenderWidgetHostViewAura::OnWindowDestroying() { LPARAM lparam = reinterpret_cast<LPARAM>(this); EnumChildWindows(parent, WindowDestroyingCallback, lparam); #endif + + // Make sure that the input method no longer references to this object before + // this object is removed from the root window (i.e. this object loses access + // to the input method). + ui::InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->DetachTextInputClient(this); } void RenderWidgetHostViewAura::OnWindowDestroyed() { @@ -2463,28 +2568,53 @@ bool RenderWidgetHostViewAura::HasHitTestMask() const { void RenderWidgetHostViewAura::GetHitTestMask(gfx::Path* mask) const { } -scoped_refptr<ui::Texture> RenderWidgetHostViewAura::CopyTexture() { - if (!host_->is_accelerated_compositing_active()) - return scoped_refptr<ui::Texture>(); - - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return scoped_refptr<ui::Texture>(); - - if (!current_surface_.get()) - return scoped_refptr<ui::Texture>(); - - WebKit::WebGLId texture_id = - gl_helper->CopyTexture(current_surface_->PrepareTexture(), - current_surface_->size()); - if (!texture_id) - return scoped_refptr<ui::Texture>(); - - return scoped_refptr<ui::Texture>( - factory->CreateOwnedTexture( +void RenderWidgetHostViewAura::DidRecreateLayer(ui::Layer *old_layer, + ui::Layer *new_layer) { + float mailbox_scale_factor; + cc::TextureMailbox old_mailbox = + old_layer->GetTextureMailbox(&mailbox_scale_factor); + scoped_refptr<ui::Texture> old_texture = old_layer->external_texture(); + // The new_layer is the one that will be used by our Window, so that's the one + // that should keep our texture. old_layer will be returned to the + // RecreateLayer caller, and should have a copy. + if (old_texture.get()) { + ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); + GLHelper* gl_helper = factory->GetGLHelper(); + scoped_refptr<ui::Texture> new_texture; + if (host_->is_accelerated_compositing_active() && + gl_helper && current_surface_.get()) { + WebKit::WebGLId texture_id = + gl_helper->CopyTexture(current_surface_->PrepareTexture(), + current_surface_->size()); + if (texture_id) { + new_texture = factory->CreateOwnedTexture( current_surface_->size(), - current_surface_->device_scale_factor(), texture_id)); + current_surface_->device_scale_factor(), texture_id); + } + } + old_layer->SetExternalTexture(new_texture); + new_layer->SetExternalTexture(old_texture); + } else if (old_mailbox.IsSharedMemory()) { + base::SharedMemory* old_buffer = old_mailbox.shared_memory(); + const size_t size = old_mailbox.shared_memory_size_in_bytes(); + + scoped_ptr<base::SharedMemory> new_buffer(new base::SharedMemory); + new_buffer->CreateAndMapAnonymous(size); + + if (old_buffer->memory() && new_buffer->memory()) { + memcpy(new_buffer->memory(), old_buffer->memory(), size); + base::SharedMemory* new_buffer_raw_ptr = new_buffer.get(); + scoped_ptr<cc::SingleReleaseCallback> callback = + cc::SingleReleaseCallback::Create(base::Bind(MailboxReleaseCallback, + Passed(&new_buffer))); + cc::TextureMailbox new_mailbox(new_buffer_raw_ptr, + old_mailbox.shared_memory_size()); + new_layer->SetTextureMailbox(new_mailbox, + callback.Pass(), + mailbox_scale_factor); + } + } + // TODO(piman): handle delegated frames. } //////////////////////////////////////////////////////////////////////////////// @@ -2859,6 +2989,18 @@ void RenderWidgetHostViewAura::OnRootWindowHostMoved( UpdateScreenInfo(window_); } +void RenderWidgetHostViewAura::ReleaseCurrentFrame() { + if (framebuffer_holder_.get() && !current_surface_.get()) { + framebuffer_holder_ = NULL; + ui::Compositor* compositor = GetCompositor(); + if (compositor) { + AddOnCommitCallbackAndDisableLocks(base::Bind( + &RenderWidgetHostViewAura::SendReclaimSoftwareFrames, AsWeakPtr())); + } + UpdateExternalTexture(); + } +} + //////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewAura, ui::CompositorObserver implementation: @@ -2908,8 +3050,17 @@ void RenderWidgetHostViewAura::OnUpdateVSyncParameters( ui::Compositor* compositor, base::TimeTicks timebase, base::TimeDelta interval) { - if (IsShowing() && !last_draw_ended_.is_null()) - host_->UpdateVSyncParameters(last_draw_ended_, interval); + if (IsShowing()) { + if (IsDeadlineSchedulingEnabled()) { + // The deadline scheduler has logic to stagger the draws of the + // Renderer and Browser built-in, so send it an accurate timebase. + host_->UpdateVSyncParameters(timebase, interval); + } else if (!last_draw_ended_.is_null()) { + // For the non-deadline scheduler, we send the Renderer an offset + // vsync timebase to avoid its draws racing the Browser's draws. + host_->UpdateVSyncParameters(last_draw_ended_, interval); + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -3021,6 +3172,7 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() { // Aura root window and we don't have a way to get an input method object // associated with the window, but just in case. DetachFromInputMethod(); + FrameMemoryManager::GetInstance()->RemoveFrame(this); } void RenderWidgetHostViewAura::UpdateCursorIfOverSelf() { @@ -3034,8 +3186,10 @@ void RenderWidgetHostViewAura::UpdateCursorIfOverSelf() { gfx::Point local_point = screen_point; local_point.Offset(-screen_rect.x(), -screen_rect.y()); - if (root_window->GetEventHandlerForPoint(local_point) != window_) + if (!root_window->HasFocus() || + root_window->GetEventHandlerForPoint(local_point) != window_) { return; + } gfx::NativeCursor cursor = current_cursor_.GetNativeCursor(); // Do not show loading cursor when the cursor is currently hidden. @@ -3064,7 +3218,7 @@ void RenderWidgetHostViewAura::FinishImeCompositionSession() { if (!has_composition_text_) return; if (host_) - host_->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), false); + host_->ImeConfirmComposition(string16(), gfx::Range::InvalidRange(), false); ImeCancelComposition(); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura.h b/chromium/content/browser/renderer_host/render_widget_host_view_aura.h index c4a16271634..f5939df0fc7 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura.h @@ -18,6 +18,7 @@ #include "cc/resources/texture_mailbox.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/aura/image_transport_factory.h" +#include "content/browser/renderer_host/frame_memory_manager.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" #include "content/common/gpu/client/gl_helper.h" @@ -59,9 +60,10 @@ namespace content { class MemoryHolder; class RenderWidgetHostImpl; class RenderWidgetHostView; +class ResizeLock; // RenderWidgetHostView class hierarchy described in render_widget_host_view.h. -class RenderWidgetHostViewAura +class CONTENT_EXPORT RenderWidgetHostViewAura : public RenderWidgetHostViewBase, public ui::CompositorObserver, public ui::TextInputClient, @@ -74,6 +76,7 @@ class RenderWidgetHostViewAura public aura::client::CursorClientObserver, public ImageTransportFactoryObserver, public BrowserAccessibilityDelegate, + public FrameContainer, public base::SupportsWeakPtr<RenderWidgetHostViewAura> { public: // Used to notify whenever the paint-content of the view changes. @@ -155,10 +158,6 @@ class RenderWidgetHostViewAura virtual bool IsShowing() OVERRIDE; virtual gfx::Rect GetViewBounds() const OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; -#if defined(OS_WIN) - virtual gfx::NativeViewAccessible AccessibleObjectFromChildId(long child_id) - OVERRIDE; -#endif // Overridden from RenderWidgetHostViewPort: virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, @@ -175,11 +174,11 @@ class RenderWidgetHostViewAura virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -192,7 +191,7 @@ class RenderWidgetHostViewAura virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -227,7 +226,7 @@ class RenderWidgetHostViewAura virtual void ProcessAckedTouchEvent( const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) OVERRIDE; - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, @@ -237,8 +236,8 @@ class RenderWidgetHostViewAura virtual void SetScrollOffsetPinning( bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE; virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; @@ -265,12 +264,12 @@ class RenderWidgetHostViewAura virtual bool GetCompositionCharacterBounds(uint32 index, gfx::Rect* rect) OVERRIDE; virtual bool HasCompositionText() OVERRIDE; - virtual bool GetTextRange(ui::Range* range) OVERRIDE; - virtual bool GetCompositionTextRange(ui::Range* range) OVERRIDE; - virtual bool GetSelectionRange(ui::Range* range) OVERRIDE; - virtual bool SetSelectionRange(const ui::Range& range) OVERRIDE; - virtual bool DeleteRange(const ui::Range& range) OVERRIDE; - virtual bool GetTextFromRange(const ui::Range& range, + virtual bool GetTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetCompositionTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetSelectionRange(gfx::Range* range) OVERRIDE; + virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; + virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; + virtual bool GetTextFromRange(const gfx::Range& range, string16* text) OVERRIDE; virtual void OnInputMethodChanged() OVERRIDE; virtual bool ChangeTextDirectionAndLayoutAlignment( @@ -302,7 +301,8 @@ class RenderWidgetHostViewAura virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE; + virtual void DidRecreateLayer(ui::Layer *old_layer, + ui::Layer *new_layer) OVERRIDE; // Overridden from ui::EventHandler: virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; @@ -329,6 +329,9 @@ class RenderWidgetHostViewAura virtual void OnRootWindowHostMoved(const aura::RootWindow* root, const gfx::Point& new_origin) OVERRIDE; + // FrameContainer implementation: + virtual void ReleaseCurrentFrame() OVERRIDE; + bool CanCopyToBitmap() const; #if defined(OS_WIN) @@ -340,25 +343,21 @@ class RenderWidgetHostViewAura protected: friend class RenderWidgetHostView; + virtual ~RenderWidgetHostViewAura(); - // Should construct only via RenderWidgetHostView::CreateViewForWidget. + // Should be constructed via RenderWidgetHostView::CreateViewForWidget. explicit RenderWidgetHostViewAura(RenderWidgetHost* host); RenderWidgetHostViewFrameSubscriber* frame_subscriber() const { return frame_subscriber_.get(); } - private: - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SetCompositionText); - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventState); - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventSyncAsync); + virtual bool ShouldCreateResizeLock(); + virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock); - class WindowObserver; - friend class WindowObserver; -#if defined(OS_WIN) - class TransientWindowObserver; - friend class TransientWindowObserver; -#endif + // Exposed for tests. + aura::Window* window() { return window_; } + gfx::Size current_frame_size() const { return current_frame_size_; } // Overridden from ui::CompositorObserver: virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE; @@ -372,6 +371,21 @@ class RenderWidgetHostViewAura base::TimeTicks timebase, base::TimeDelta interval) OVERRIDE; + private: + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SetCompositionText); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventState); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventSyncAsync); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SwapNotifiesWindow); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, + SkippedDelegatedFrames); + + class WindowObserver; + friend class WindowObserver; +#if defined(OS_WIN) + class TransientWindowObserver; + friend class TransientWindowObserver; +#endif + // Overridden from ImageTransportFactoryObserver: virtual void OnLostResources() OVERRIDE; @@ -387,8 +401,6 @@ class RenderWidgetHostViewAura virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE; virtual void FatalAccessibilityTreeError() OVERRIDE; - virtual ~RenderWidgetHostViewAura(); - void UpdateCursorIfOverSelf(); bool ShouldSkipFrame(gfx::Size size_in_dip) const; @@ -509,7 +521,9 @@ class RenderWidgetHostViewAura scoped_ptr<cc::SoftwareFrameData> frame_data, float frame_device_scale_factor, const ui::LatencyInfo& latency_info); - void SendSoftwareFrameAck(uint32 output_surface_id, + void SendSoftwareFrameAck(uint32 output_surface_id); + void SendReclaimSoftwareFrames(); + void ReleaseSoftwareFrame(uint32 output_surface_id, unsigned software_frame_id); void DidReceiveFrameFromRenderer(); @@ -565,6 +579,8 @@ class RenderWidgetHostViewAura // The current text input type. ui::TextInputType text_input_type_; + // The current text input mode corresponding to HTML5 inputmode attribute. + ui::TextInputMode text_input_mode_; bool can_compose_inline_; // Rectangles for the selection anchor and focus. @@ -588,12 +604,21 @@ class RenderWidgetHostViewAura // This holds the current software framebuffer. scoped_refptr<MemoryHolder> framebuffer_holder_; + // With delegated renderer, this is the last output surface, used to + // disambiguate resources with the same id coming from different output + // surfaces. + uint32 last_output_surface_id_; + // The damage in the previously presented buffer. SkRegion previous_damage_; // Pending damage from previous frames that we skipped. SkRegion skipped_damage_; + // True after a delegated frame has been skipped, until a frame is not + // skipped. + bool skipped_frames_; + // The size of the last frame that was swapped (even if we skipped it). // Used to determine when the skipped_damage_ needs to be reset due to // size changes between front- and backbuffer. @@ -625,9 +650,6 @@ class RenderWidgetHostViewAura // software backing store is updated. bool accelerated_compositing_state_changed_; - // Used to prevent further resizes while a resize is pending. - class ResizeLock; - // This lock is the one waiting for a frame of the right size to come back // from the renderer/GPU process. It is set from the moment the aura window // got resized, to the moment we committed the renderer frame of the same @@ -700,6 +722,14 @@ class RenderWidgetHostViewAura ui::LatencyInfo software_latency_info_; + struct ReleasedFrameInfo { + ReleasedFrameInfo(uint32 output_id, unsigned software_frame_id) + : output_surface_id(output_id), frame_id(software_frame_id) {} + uint32 output_surface_id; + unsigned frame_id; + }; + std::vector<ReleasedFrameInfo> released_software_frames_; + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura); }; diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index 03f7d5c591f..824db746ceb 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc @@ -5,16 +5,24 @@ #include "content/browser/renderer_host/render_widget_host_view_aura.h" #include "base/basictypes.h" +#include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "cc/output/compositor_frame.h" +#include "cc/output/compositor_frame_metadata.h" +#include "cc/output/gl_frame_data.h" +#include "content/browser/aura/resize_lock.h" +#include "content/browser/browser_thread_impl.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" #include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/common/gpu/gpu_messages.h" #include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "ipc/ipc_test_sink.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" @@ -26,9 +34,11 @@ #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/ui_base_types.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" + +using testing::_; namespace content { namespace { @@ -69,11 +79,48 @@ class TestWindowObserver : public aura::WindowObserver { DISALLOW_COPY_AND_ASSIGN(TestWindowObserver); }; +class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura { + public: + FakeRenderWidgetHostViewAura(RenderWidgetHost* widget) + : RenderWidgetHostViewAura(widget), has_resize_lock_(false) {} + + virtual ~FakeRenderWidgetHostViewAura() {} + + virtual bool ShouldCreateResizeLock() OVERRIDE { + gfx::Size desired_size = window()->bounds().size(); + return desired_size != current_frame_size(); + } + + virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock) + OVERRIDE { + gfx::Size desired_size = window()->bounds().size(); + return scoped_ptr<ResizeLock>( + new FakeResizeLock(desired_size, defer_compositor_lock)); + } + + void RunOnCompositingDidCommit() { + OnCompositingDidCommit(window()->GetRootWindow()->compositor()); + } + + // A lock that doesn't actually do anything to the compositor, and does not + // time out. + class FakeResizeLock : public ResizeLock { + public: + FakeResizeLock(const gfx::Size new_size, bool defer_compositor_lock) + : ResizeLock(new_size, defer_compositor_lock) {} + }; + + bool has_resize_lock_; + gfx::Size last_frame_size_; +}; + class RenderWidgetHostViewAuraTest : public testing::Test { public: - RenderWidgetHostViewAuraTest() {} + RenderWidgetHostViewAuraTest() + : browser_thread_for_ui_(BrowserThread::UI, &message_loop_) {} virtual void SetUp() { + ImageTransportFactory::InitializeForUnitTests(); aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); aura_test_helper_->SetUp(); @@ -84,7 +131,7 @@ class RenderWidgetHostViewAuraTest : public testing::Test { sink_ = &process_host->sink(); parent_host_ = new RenderWidgetHostImpl( - &delegate_, process_host, MSG_ROUTING_NONE); + &delegate_, process_host, MSG_ROUTING_NONE, false); parent_view_ = static_cast<RenderWidgetHostViewAura*>( RenderWidgetHostView::CreateViewForWidget(parent_host_)); parent_view_->InitAsChild(NULL); @@ -92,10 +139,11 @@ class RenderWidgetHostViewAuraTest : public testing::Test { aura_test_helper_->root_window(), gfx::Rect()); widget_host_ = new RenderWidgetHostImpl( - &delegate_, process_host, MSG_ROUTING_NONE); + &delegate_, process_host, MSG_ROUTING_NONE, false); widget_host_->Init(); - view_ = static_cast<RenderWidgetHostViewAura*>( - RenderWidgetHostView::CreateViewForWidget(widget_host_)); + widget_host_->OnMessageReceived( + ViewHostMsg_DidActivateAcceleratedCompositing(0, true)); + view_ = new FakeRenderWidgetHostViewAura(widget_host_); } virtual void TearDown() { @@ -112,10 +160,12 @@ class RenderWidgetHostViewAuraTest : public testing::Test { message_loop_.DeleteSoon(FROM_HERE, browser_context_.release()); message_loop_.RunUntilIdle(); + ImageTransportFactory::Terminate(); } protected: base::MessageLoopForUI message_loop_; + BrowserThreadImpl browser_thread_for_ui_; scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_; scoped_ptr<BrowserContext> browser_context_; MockRenderWidgetHostDelegate delegate_; @@ -128,7 +178,7 @@ class RenderWidgetHostViewAuraTest : public testing::Test { // Tests should set these to NULL if they've already triggered their // destruction. RenderWidgetHostImpl* widget_host_; - RenderWidgetHostViewAura* view_; + FakeRenderWidgetHostViewAura* view_; IPC::TestSink* sink_; @@ -170,6 +220,11 @@ class FullscreenLayoutManager : public aura::LayoutManager { DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager); }; +class MockWindowObserver : public aura::WindowObserver { + public: + MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&)); +}; + } // namespace // Checks that a fullscreen view has the correct show-state and receives the @@ -231,7 +286,7 @@ TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) { const ui::CompositionUnderlines& underlines = composition_text.underlines; // Caret is at the end. (This emulates Japanese MSIME 2007 and later) - composition_text.selection = ui::Range(4); + composition_text.selection = gfx::Range(4); sink_->ClearMessages(); view_->SetCompositionText(composition_text); @@ -584,4 +639,209 @@ TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) { } } +scoped_ptr<cc::CompositorFrame> MakeGLFrame(float scale_factor, + gfx::Size size, + gfx::Rect damage) { + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + frame->metadata.device_scale_factor = scale_factor; + frame->gl_frame_data.reset(new cc::GLFrameData); + frame->gl_frame_data->sync_point = 1; + memset(frame->gl_frame_data->mailbox.name, '1', 64); + frame->gl_frame_data->size = size; + frame->gl_frame_data->sub_buffer_rect = damage; + return frame.Pass(); +} + +scoped_ptr<cc::CompositorFrame> MakeSoftwareFrame(float scale_factor, + gfx::Size size, + gfx::Rect damage) { + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + frame->metadata.device_scale_factor = scale_factor; + frame->software_frame_data.reset(new cc::SoftwareFrameData); + frame->software_frame_data->id = 1; + frame->software_frame_data->size = size; + frame->software_frame_data->damage_rect = damage; + base::SharedMemory shm; + shm.CreateAndMapAnonymous(size.GetArea() * 4); + shm.GiveToProcess(base::GetCurrentProcessHandle(), + &frame->software_frame_data->handle); + return frame.Pass(); +} + +scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor, + gfx::Size size, + gfx::Rect damage) { + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + frame->metadata.device_scale_factor = scale_factor; + frame->delegated_frame_data.reset(new cc::DelegatedFrameData); + + scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create(); + pass->SetNew(cc::RenderPass::Id(1, 1), + gfx::Rect(size), + gfx::RectF(damage), + gfx::Transform()); + frame->delegated_frame_data->render_pass_list.push_back(pass.Pass()); + return frame.Pass(); +} + +// Swapping a frame should notify the window. +TEST_F(RenderWidgetHostViewAuraTest, SwapNotifiesWindow) { + gfx::Size view_size(100, 100); + gfx::Rect view_rect(view_size); + + view_->InitAsChild(NULL); + view_->GetNativeView()->SetDefaultParentByRootWindow( + parent_view_->GetNativeView()->GetRootWindow(), gfx::Rect()); + view_->SetSize(view_size); + view_->WasShown(); + + MockWindowObserver observer; + view_->window_->AddObserver(&observer); + + // Swap a frame through the GPU path. + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_id = widget_host_->surface_id(); + params.route_id = widget_host_->GetRoutingID(); + params.mailbox_name = std::string(64, '1'); + params.size = view_size; + params.scale_factor = 1.f; + + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->AcceleratedSurfaceBuffersSwapped(params, 0); + testing::Mock::VerifyAndClearExpectations(&observer); + + // DSF = 2 + params.size = gfx::Size(200, 200); + params.scale_factor = 2.f; + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->AcceleratedSurfaceBuffersSwapped(params, 0); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Partial frames though GPU path + GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params post_params; + post_params.surface_id = widget_host_->surface_id(); + post_params.route_id = widget_host_->GetRoutingID(); + post_params.mailbox_name = std::string(64, '1'); + post_params.surface_size = gfx::Size(200, 200); + post_params.surface_scale_factor = 2.f; + post_params.x = 40; + post_params.y = 40; + post_params.width = 80; + post_params.height = 80; + // rect from params is upside down, and is inflated in RWHVA, just because. + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(19, 39, 42, 42))); + view_->AcceleratedSurfacePostSubBuffer(post_params, 0); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Composite-to-mailbox path + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame(0, MakeGLFrame(1.f, view_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + + // rect from GL frame is upside down, and is inflated in RWHVA, just because. + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(4, 89, 7, 7))); + view_->OnSwapCompositorFrame( + 0, MakeGLFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Software path + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame(0, MakeSoftwareFrame(1.f, view_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(5, 5, 5, 5))); + view_->OnSwapCompositorFrame( + 0, MakeSoftwareFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Delegated renderer path + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, view_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(5, 5, 5, 5))); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); + testing::Mock::VerifyAndClearExpectations(&observer); + + view_->window_->RemoveObserver(&observer); +} + +// Skipped frames should not drop their damage. +TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) { + gfx::Rect view_rect(100, 100); + gfx::Size frame_size = view_rect.size(); + + view_->InitAsChild(NULL); + view_->GetNativeView()->SetDefaultParentByRootWindow( + parent_view_->GetNativeView()->GetRootWindow(), gfx::Rect()); + view_->SetSize(view_rect.size()); + + MockWindowObserver observer; + view_->window_->AddObserver(&observer); + + // A full frame of damage. + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // A partial damage frame. + gfx::Rect partial_view_rect(30, 30, 20, 20); + EXPECT_CALL(observer, + OnWindowPaintScheduled(view_->window_, partial_view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, partial_view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // Lock the compositor. Now we should drop frames. + view_rect = gfx::Rect(150, 150); + view_->SetSize(view_rect.size()); + view_->MaybeCreateResizeLock(); + + // This frame is dropped. + gfx::Rect dropped_damage_rect_1(10, 20, 30, 40); + EXPECT_CALL(observer, OnWindowPaintScheduled(_, _)).Times(0); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, dropped_damage_rect_1)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + gfx::Rect dropped_damage_rect_2(40, 50, 10, 20); + EXPECT_CALL(observer, OnWindowPaintScheduled(_, _)).Times(0); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, dropped_damage_rect_2)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // Unlock the compositor. This frame should damage everything. + frame_size = view_rect.size(); + + gfx::Rect new_damage_rect(5, 6, 10, 10); + EXPECT_CALL(observer, + OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, new_damage_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // A partial damage frame, this should not be dropped. + EXPECT_CALL(observer, + OnWindowPaintScheduled(view_->window_, partial_view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, partial_view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + + view_->window_->RemoveObserver(&observer); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_base.cc b/chromium/content/browser/renderer_host/render_widget_host_view_base.cc index d6b1e789e2a..57181031f22 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_base.cc @@ -11,7 +11,7 @@ #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/port/browser/render_widget_host_view_frame_subscriber.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/port/browser/synthetic_gesture.h" #include "third_party/WebKit/public/web/WebScreenInfo.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" @@ -29,9 +29,9 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h" #include "content/public/common/content_switches.h" -#include "ui/base/win/dpi.h" -#include "ui/base/win/hwnd_util.h" #include "ui/gfx/gdi_util.h" +#include "ui/gfx/win/dpi.h" +#include "ui/gfx/win/hwnd_util.h" #endif #if defined(TOOLKIT_GTK) @@ -118,7 +118,7 @@ LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, } bool IsPluginWrapperWindow(HWND window) { - return ui::GetClassNameW(window) == + return gfx::GetClassNameW(window) == string16(kWrapperNativeWindowClassName); } @@ -151,7 +151,7 @@ HWND ReparentWindow(HWND window, HWND parent) { MAKEINTATOM(atom), 0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, parent, 0, instance, 0); - ui::CheckWindowCreated(new_parent); + gfx::CheckWindowCreated(new_parent); ::SetParent(window, new_parent); // How many times we try to find a PluginProcessHost whose process matches // the HWND. @@ -169,7 +169,7 @@ BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) { return TRUE; gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); - gfx::Rect rect_in_pixels = ui::win::DIPToScreenRect(*rect); + gfx::Rect rect_in_pixels = gfx::win::DIPToScreenRect(*rect); static UINT msg = RegisterWindowMessage(kPaintMessageName); WPARAM wparam = MAKEWPARAM(rect_in_pixels.x(), rect_in_pixels.y()); lparam = MAKELPARAM(rect_in_pixels.width(), rect_in_pixels.height()); @@ -284,7 +284,7 @@ void RenderWidgetHostViewBase::MovePluginWindowsHelper( #endif if (move.rects_valid) { - gfx::Rect clip_rect_in_pixel = ui::win::DIPToScreenRect(move.clip_rect); + gfx::Rect clip_rect_in_pixel = gfx::win::DIPToScreenRect(move.clip_rect); HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(), clip_rect_in_pixel.y(), clip_rect_in_pixel.right(), @@ -313,7 +313,7 @@ void RenderWidgetHostViewBase::MovePluginWindowsHelper( } gfx::Rect window_rect_in_pixel = - ui::win::DIPToScreenRect(move.window_rect); + gfx::win::DIPToScreenRect(move.window_rect); defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, window, NULL, window_rect_in_pixel.x(), @@ -382,7 +382,7 @@ RenderWidgetHostViewBase::RenderWidgetHostViewBase() mouse_locked_(false), showing_context_menu_(false), selection_text_offset_(0), - selection_range_(ui::Range::InvalidRange()), + selection_range_(gfx::Range::InvalidRange()), current_device_scale_factor_(0), renderer_frame_number_(0) { } @@ -417,7 +417,7 @@ float RenderWidgetHostViewBase::GetOverdrawBottomHeight() const { void RenderWidgetHostViewBase::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { selection_text_ = text; selection_text_offset_ = offset; selection_range_.set_start(range.start()); @@ -501,13 +501,21 @@ bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) { return true; } -SmoothScrollGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture( +SyntheticGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) { return new BasicMouseWheelSmoothScrollGesture(scroll_down, pixels_to_scroll, mouse_event_x, mouse_event_y); } +SyntheticGesture* RenderWidgetHostViewBase::CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) { + // There is no generic implementation for pinch gestures. + NOTIMPLEMENTED(); + return NULL; +} + void RenderWidgetHostViewBase::ProcessAckedTouchEvent( const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) { } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_base.h b/chromium/content/browser/renderer_host/render_widget_host_view_base.h index 68fcaffaea7..0472153b275 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_base.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_base.h @@ -20,8 +20,8 @@ #include "base/callback_forward.h" #include "content/common/content_export.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "ui/base/range/range.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/range/range.h" #include "ui/gfx/rect.h" namespace content { @@ -48,7 +48,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; virtual const SkBitmap& GetBackground() OVERRIDE; virtual gfx::Size GetPhysicalBackingSize() const OVERRIDE; @@ -69,9 +69,12 @@ class CONTENT_EXPORT RenderWidgetHostViewBase GetBrowserAccessibilityManager() const OVERRIDE; virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) OVERRIDE; - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) OVERRIDE; + virtual SyntheticGesture* CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) OVERRIDE; virtual bool CanSubscribeFrame() const OVERRIDE; virtual void BeginFrameSubscription( scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE; @@ -144,7 +147,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase size_t selection_text_offset_; // The current selection range relative to the start of the web page. - ui::Range selection_range_; + gfx::Range selection_range_; protected: // The scale factor of the display the renderer is currently on. diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc index 2b47ff19315..0fe376e65b3 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc @@ -6,29 +6,30 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/port/browser/render_widget_host_view_frame_subscriber.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "media/base/video_frame.h" #include "media/filters/skcanvas_video_renderer.h" #include "net/base/net_util.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkDevice.h" #include "ui/base/ui_base_switches.h" #include "ui/gfx/size_conversions.h" +#include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" #if defined(OS_MACOSX) @@ -36,7 +37,8 @@ #endif #if defined(OS_WIN) -#include "ui/base/win/dpi.h" +#include "base/win/windows_version.h" +#include "ui/gfx/win/dpi.h" #endif namespace content { @@ -341,6 +343,14 @@ class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber { // is enabled. IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, CopyFromBackingStore) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + RunBasicCopyFromBackingStoreTest(); } @@ -373,8 +383,18 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, // Tests that the callback passed to CopyFromCompositingSurfaceToVideoFrame is // always called, even when the RenderWidgetHost is deleting in the middle of // an async copy. +// +// Test is flaky on Win Aura. http://crbug.com/276783 +#if (defined(OS_WIN) && defined(USE_AURA)) || \ + (defined(OS_CHROMEOS) && !defined(NDEBUG)) +#define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \ + DISABLED_CopyFromCompositingSurface_CallbackDespiteDelete +#else +#define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \ + CopyFromCompositingSurface_CallbackDespiteDelete +#endif IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, - CopyFromCompositingSurface_CallbackDespiteDelete) { + MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete) { SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort(); if (!view->CanCopyToVideoFrame()) { @@ -410,6 +430,14 @@ IN_PROC_BROWSER_TEST_F(NonCompositingRenderWidgetHostViewBrowserTest, // until at least one DeliverFrameCallback has been invoked. IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, FrameSubscriberTest) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort(); if (!view->CanSubscribeFrame()) { @@ -435,6 +463,14 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, // Test that we can copy twice from an accelerated composited page. IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, CopyTwice) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort(); if (!view->CanCopyToVideoFrame()) { @@ -558,7 +594,7 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture bitmap.allocPixels(); bitmap.setIsOpaque(true); - SkDevice device(bitmap); + SkBitmapDevice device(bitmap); SkCanvas canvas(&device); video_renderer.Paint(video_frame.get(), @@ -638,7 +674,8 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture // The page is loaded in the renderer, wait for a new frame to arrive. uint32 frame = rwhvp->RendererFrameNumber(); - GetRenderWidgetHost()->ScheduleComposite(); + while (!GetRenderWidgetHost()->ScheduleComposite()) + GiveItSomeTime(); while (rwhvp->RendererFrameNumber() == frame) GiveItSomeTime(); @@ -824,7 +861,7 @@ class CompositingRenderWidgetHostViewTabCaptureHighDPI base::StringPrintf("%f", scale())); #if defined(OS_WIN) cmd->AppendSwitchASCII(switches::kHighDPISupport, "1"); - ui::EnableHighDPISupport(); + gfx::EnableHighDPISupport(); #endif } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc index ddaf9528cfd..61512ef7506 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -29,6 +29,7 @@ #include "content/browser/renderer_host/gtk_im_context_wrapper.h" #include "content/browser/renderer_host/gtk_key_bindings_handler.h" #include "content/browser/renderer_host/gtk_window_utils.h" +#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/gpu/gpu_messages.h" @@ -40,17 +41,15 @@ #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebScreenInfo.h" -#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" -#include "ui/base/gtk/gtk_compat.h" -#include "ui/base/text/text_elider.h" #include "ui/base/x/active_window_watcher_x.h" #include "ui/base/x/x11_util.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/gtk_native_view_id_manager.h" #include "ui/gfx/gtk_preserve_window.h" +#include "ui/gfx/text_elider.h" #include "webkit/common/cursors/webcursor_gtk_data.h" -using WebKit::WebInputEventFactory; using WebKit::WebMouseWheelEvent; using WebKit::WebScreenInfo; @@ -70,10 +69,6 @@ namespace { const int kMaxWindowWidth = 10000; const int kMaxWindowHeight = 10000; -// See WebInputEventFactor.cpp for a reason for this being the default -// scroll size for linux. -const float kDefaultScrollPixelsPerTick = 160.0f / 3.0f; - const GdkColor kBGColor = #if defined(NDEBUG) { 0, 0xff * 257, 0xff * 257, 0xff * 257 }; @@ -354,7 +349,7 @@ class RenderWidgetHostViewGtkWidget { RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); if (widget_host) - widget_host->ForwardMouseEvent(WebInputEventFactory::mouseEvent(event)); + widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event)); // Although we did handle the mouse event, we need to let other handlers // run (in particular the one installed by WebContentsViewGtk). @@ -380,8 +375,7 @@ class RenderWidgetHostViewGtkWidget { host_view->ModifyEventForEdgeDragging(widget, event); - WebKit::WebMouseEvent mouse_event = - WebInputEventFactory::mouseEvent(event); + WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); if (host_view->mouse_locked_) { gfx::Point center = host_view->GetWidgetCenter(); @@ -431,8 +425,7 @@ class RenderWidgetHostViewGtkWidget { // additionally send this crossing event with the state indicating the // button is down, it causes problems with drag and drop in WebKit.) if (!(event->state & any_button_mask)) { - WebKit::WebMouseEvent mouse_event = - WebInputEventFactory::mouseEvent(event); + WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); host_view->ModifyEventMovementAndCoords(&mouse_event); // When crossing out and back into a render view the movement values // must represent the instantaneous movement of the mouse, not the jump @@ -500,7 +493,7 @@ class RenderWidgetHostViewGtkWidget { gdk_event_put(event); gdk_event_free(event); } - return num_clicks * kDefaultScrollPixelsPerTick; + return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); } static gboolean OnMouseScrollEvent(GtkWidget* widget, @@ -518,21 +511,23 @@ class RenderWidgetHostViewGtkWidget { event->direction = GDK_SCROLL_RIGHT; } - WebMouseWheelEvent web_event = WebInputEventFactory::mouseWheelEvent(event); + WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event); + const float pixelsPerTick = + WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); // We peek ahead at the top of the queue to look for additional pending // scroll events. if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN) { if (event->direction == GDK_SCROLL_UP) - web_event.deltaY = kDefaultScrollPixelsPerTick; + web_event.deltaY = pixelsPerTick; else - web_event.deltaY = -kDefaultScrollPixelsPerTick; + web_event.deltaY = -pixelsPerTick; web_event.deltaY += GetPendingScrollDelta(true, event->state); } else { if (event->direction == GDK_SCROLL_LEFT) - web_event.deltaX = kDefaultScrollPixelsPerTick; + web_event.deltaX = pixelsPerTick; else - web_event.deltaX = -kDefaultScrollPixelsPerTick; + web_event.deltaX = -pixelsPerTick; web_event.deltaX += GetPendingScrollDelta(false, event->state); } RenderWidgetHostImpl::From( @@ -546,7 +541,7 @@ class RenderWidgetHostViewGtkWidget { RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) : host_(RenderWidgetHostImpl::From(widget_host)), about_to_validate_and_paint_(false), - is_hidden_(false), + is_hidden_(host_->is_hidden()), is_loading_(false), parent_(NULL), is_popup_first_mouse_release_(true), @@ -686,17 +681,18 @@ RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const { } void RenderWidgetHostViewGtk::WasShown() { - if (!is_hidden_) + if (!host_ || !is_hidden_) return; if (web_contents_switch_paint_time_.is_null()) web_contents_switch_paint_time_ = base::TimeTicks::Now(); is_hidden_ = false; + host_->WasShown(); } void RenderWidgetHostViewGtk::WasHidden() { - if (is_hidden_) + if (!host_ || is_hidden_) return; // If we receive any more paint messages while we are hidden, we want to @@ -795,10 +791,12 @@ bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const { void RenderWidgetHostViewGtk::Show() { gtk_widget_show(view_.get()); + WasShown(); } void RenderWidgetHostViewGtk::Hide() { gtk_widget_hide(view_.get()); + WasHidden(); } bool RenderWidgetHostViewGtk::IsShowing() { @@ -837,8 +835,8 @@ void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) { void RenderWidgetHostViewGtk::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { im_context_->UpdateInputMethodState(type, can_compose_inline); } @@ -936,7 +934,7 @@ void RenderWidgetHostViewGtk::SetTooltipText(const string16& tooltip_text) { // this itself). // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream. const string16 clamped_tooltip = - ui::TruncateString(tooltip_text, kMaxTooltipLength); + gfx::TruncateString(tooltip_text, kMaxTooltipLength); if (clamped_tooltip.empty()) { gtk_widget_set_has_tooltip(view_.get(), FALSE); @@ -948,7 +946,7 @@ void RenderWidgetHostViewGtk::SetTooltipText(const string16& tooltip_text) { void RenderWidgetHostViewGtk::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { RenderWidgetHostViewBase::SelectionChanged(text, offset, range); if (text.empty() || range.is_empty()) @@ -1542,8 +1540,8 @@ void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() { } } -void RenderWidgetHostViewGtk::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewGtk::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (!GetBrowserAccessibilityManager()) { GtkWidget* parent = gtk_widget_get_parent(view_.get()); SetBrowserAccessibilityManager( @@ -1552,7 +1550,7 @@ void RenderWidgetHostViewGtk::OnAccessibilityNotifications( BrowserAccessibilityManagerGtk::GetEmptyDocument(), this)); } - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } AtkObject* RenderWidgetHostViewGtk::GetAccessible() { diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h index 9ff410a07b1..fc57a1a5aad 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h @@ -17,8 +17,6 @@ #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" #include "ipc/ipc_sender.h" -#include "ui/base/animation/animation_delegate.h" -#include "ui/base/animation/slide_animation.h" #include "ui/base/gtk/gtk_signal.h" #include "ui/base/gtk/gtk_signal_registrar.h" #include "ui/base/gtk/owned_widget_gtk.h" @@ -83,8 +81,8 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -98,7 +96,7 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -131,8 +129,8 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; // ActiveWindowWatcherXObserver implementation. diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc index b49ca41054e..05730b92545 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc @@ -51,7 +51,7 @@ RenderWidgetHostViewGuest::RenderWidgetHostViewGuest( RenderWidgetHostView* platform_view) : host_(RenderWidgetHostImpl::From(widget_host)), guest_(guest), - is_hidden_(false), + is_hidden_(host_->is_hidden()), platform_view_(static_cast<RenderWidgetHostViewPort*>(platform_view)) { #if defined(OS_WIN) || defined(USE_AURA) gesture_recognizer_.reset(ui::GestureRecognizer::Create(this)); @@ -289,11 +289,11 @@ void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading) { void RenderWidgetHostViewGuest::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { RenderWidgetHostViewPort::FromRWHV( guest_->GetEmbedderRenderWidgetHostView())-> - TextInputTypeChanged(type, can_compose_inline, input_mode); + TextInputTypeChanged(type, input_mode, can_compose_inline); } void RenderWidgetHostViewGuest::ImeCancelComposition() { @@ -302,7 +302,7 @@ void RenderWidgetHostViewGuest::ImeCancelComposition() { #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) void RenderWidgetHostViewGuest::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { } #endif @@ -317,7 +317,7 @@ void RenderWidgetHostViewGuest::DidUpdateBackingStore( void RenderWidgetHostViewGuest::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { platform_view_->SelectionChanged(text, offset, range); } @@ -375,14 +375,6 @@ void RenderWidgetHostViewGuest::SetClickthroughRegion(SkRegion* region) { } #endif -#if defined(OS_WIN) && defined(USE_AURA) -gfx::NativeViewAccessible -RenderWidgetHostViewGuest::AccessibleObjectFromChildId(long child_id) { - NOTIMPLEMENTED(); - return NULL; -} -#endif - void RenderWidgetHostViewGuest::SetHasHorizontalScrollbar( bool has_horizontal_scrollbar) { platform_view_->SetHasHorizontalScrollbar(has_horizontal_scrollbar); @@ -412,8 +404,8 @@ void RenderWidgetHostViewGuest::GetScreenInfo(WebKit::WebScreenInfo* results) { embedder_view->GetScreenInfo(results); } -void RenderWidgetHostViewGuest::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewGuest::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { } #if defined(OS_MACOSX) diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest.h b/chromium/content/browser/renderer_host/render_widget_host_view_guest.h index 15b0acc1a8b..82f2eab1bac 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_guest.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_guest.h @@ -10,9 +10,9 @@ #include "base/memory/scoped_ptr.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" -#include "ui/base/events/event.h" #include "ui/base/gestures/gesture_recognizer.h" #include "ui/base/gestures/gesture_types.h" +#include "ui/events/event.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/vector2d_f.h" @@ -65,10 +65,6 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest #if defined(OS_WIN) && !defined(USE_AURA) virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE; #endif -#if defined(OS_WIN) && defined(USE_AURA) - virtual gfx::NativeViewAccessible AccessibleObjectFromChildId(long child_id) - OVERRIDE; -#endif // RenderWidgetHostViewPort implementation. virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, @@ -85,12 +81,12 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; #endif virtual void DidUpdateBackingStore( @@ -105,7 +101,7 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -146,8 +142,8 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; virtual void GetScreenInfo(WebKit::WebScreenInfo* results) OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; #if defined(OS_MACOSX) diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc index 959f5e404a3..a112634b69d 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc @@ -32,7 +32,7 @@ class RenderWidgetHostViewGuestTest : public testing::Test { MockRenderProcessHost* process_host = new MockRenderProcessHost(browser_context_.get()); widget_host_ = new RenderWidgetHostImpl( - &delegate_, process_host, MSG_ROUTING_NONE); + &delegate_, process_host, MSG_ROUTING_NONE, false); view_ = new RenderWidgetHostViewGuest( widget_host_, NULL, new TestRenderWidgetHostView(widget_host_)); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac.h b/chromium/content/browser/renderer_host/render_widget_host_view_mac.h index 6cbe6dc7b69..11b1d973a42 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac.h @@ -248,11 +248,11 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -265,7 +265,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -284,8 +284,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE; virtual void EndFrameSubscription() OVERRIDE; virtual void OnAcceleratedCompositingStateChange() OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params ) OVERRIDE; virtual bool PostProcessEventForPluginIme( const NativeWebKeyboardEvent& event) OVERRIDE; @@ -354,19 +354,19 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, // point to |line_breaking_point|. The |line_break_point| is valid only if // this function returns true. bool GetLineBreakIndex(const std::vector<gfx::Rect>& bounds, - const ui::Range& range, + const gfx::Range& range, size_t* line_break_point); // Returns composition character boundary rectangle. The |range| is // composition based range. Also stores |actual_range| which is corresponding // to actually used range for returned rectangle. - gfx::Rect GetFirstRectForCompositionRange(const ui::Range& range, - ui::Range* actual_range); + gfx::Rect GetFirstRectForCompositionRange(const gfx::Range& range, + gfx::Range* actual_range); // Converts from given whole character range to composition oriented range. If - // the conversion failed, return ui::Range::InvalidRange. - ui::Range ConvertCharacterRangeToCompositionRange( - const ui::Range& request_range); + // the conversion failed, return gfx::Range::InvalidRange. + gfx::Range ConvertCharacterRangeToCompositionRange( + const gfx::Range& request_range); // These member variables should be private, but the associated ObjC class // needs access to them and can't be made a friend. @@ -548,7 +548,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, base::Time next_swap_ack_time_; // The current composition character range and its bounds. - ui::Range composition_range_; + gfx::Range composition_range_; std::vector<gfx::Rect> composition_bounds_; // The current caret bounds. diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm index 07448884b5d..b6a6c180bdf 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -7,8 +7,9 @@ #import <objc/runtime.h> #include <QuartzCore/QuartzCore.h> +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/crash_logging.h" #include "base/debug/trace_event.h" @@ -54,7 +55,7 @@ #include "ui/base/cocoa/animation_utils.h" #import "ui/base/cocoa/fullscreen_window_manager.h" #import "ui/base/cocoa/underlay_opengl_hosting_window.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/base/layout.h" #include "ui/gfx/display.h" #include "ui/gfx/point.h" @@ -425,7 +426,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) allow_overlapping_views_(false), use_core_animation_(false), is_loading_(false), - is_hidden_(false), + is_hidden_(render_widget_host_->is_hidden()), weak_factory_(this), fullscreen_parent_host_view_(NULL), pending_swap_buffers_acks_weak_factory_(this), @@ -869,6 +870,10 @@ void RenderWidgetHostViewMac::Show() { } void RenderWidgetHostViewMac::Hide() { + // We're messing with the window, so do this to ensure no flashes. + if (!use_core_animation_) + [[cocoa_view_ window] disableScreenUpdatesUntilFlush]; + [cocoa_view_ setHidden:YES]; WasHidden(); @@ -905,8 +910,8 @@ void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) { void RenderWidgetHostViewMac::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (text_input_type_ != type || can_compose_inline_ != can_compose_inline) { text_input_type_ = type; @@ -930,7 +935,7 @@ void RenderWidgetHostViewMac::ImeCancelComposition() { } void RenderWidgetHostViewMac::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { // The RangeChanged message is only sent with valid values. The current // caret position (start == end) will be sent if there is no IME range. @@ -1074,7 +1079,7 @@ void RenderWidgetHostViewMac::StopSpeaking() { // void RenderWidgetHostViewMac::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { if (range.is_empty() || text.empty()) { selected_text_.clear(); } else { @@ -1161,7 +1166,7 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurface( gfx::Size dst_pixel_size = gfx::ToFlooredSize( gfx::ScaleSize(dst_size, scale)); - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect), dst_pixel_size, @@ -1192,7 +1197,7 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame( if (src_subrect.IsEmpty()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); compositing_iosurface_->CopyToVideoFrame( GetScaledOpenGLPixelRect(src_subrect), target, @@ -1485,7 +1490,7 @@ void RenderWidgetHostViewMac::GetVSyncParameters( bool RenderWidgetHostViewMac::GetLineBreakIndex( const std::vector<gfx::Rect>& bounds, - const ui::Range& range, + const gfx::Range& range, size_t* line_break_point) { DCHECK(line_break_point); if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty()) @@ -1515,8 +1520,8 @@ bool RenderWidgetHostViewMac::GetLineBreakIndex( } gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( - const ui::Range& range, - ui::Range* actual_range) { + const gfx::Range& range, + gfx::Range* actual_range) { DCHECK(actual_range); DCHECK(!composition_bounds_.empty()); DCHECK(range.start() <= composition_bounds_.size()); @@ -1541,7 +1546,7 @@ gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) { end_idx = range.end(); } - *actual_range = ui::Range(range.start(), end_idx); + *actual_range = gfx::Range(range.start(), end_idx); gfx::Rect rect = composition_bounds_[range.start()]; for (size_t i = range.start() + 1; i < end_idx; ++i) { rect.Union(composition_bounds_[i]); @@ -1549,21 +1554,21 @@ gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( return rect; } -ui::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange( - const ui::Range& request_range) { +gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange( + const gfx::Range& request_range) { if (composition_range_.is_empty()) - return ui::Range::InvalidRange(); + return gfx::Range::InvalidRange(); if (request_range.is_reversed()) - return ui::Range::InvalidRange(); + return gfx::Range::InvalidRange(); if (request_range.start() < composition_range_.start() || request_range.start() > composition_range_.end() || request_range.end() > composition_range_.end()) { - return ui::Range::InvalidRange(); + return gfx::Range::InvalidRange(); } - return ui::Range( + return gfx::Range( request_range.start() - composition_range_.start(), request_range.end() - composition_range_.start()); } @@ -1578,16 +1583,16 @@ bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange( "RenderWidgetHostViewMac::GetFirstRectForCharacterRange"); // If requested range is same as caret location, we can just return it. - if (selection_range_.is_empty() && ui::Range(range) == selection_range_) { + if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) { if (actual_range) *actual_range = range; *rect = NSRectFromCGRect(caret_rect_.ToCGRect()); return true; } - const ui::Range request_range_in_composition = - ConvertCharacterRangeToCompositionRange(ui::Range(range)); - if (request_range_in_composition == ui::Range::InvalidRange()) + const gfx::Range request_range_in_composition = + ConvertCharacterRangeToCompositionRange(gfx::Range(range)); + if (request_range_in_composition == gfx::Range::InvalidRange()) return false; // If firstRectForCharacterRange in WebFrame is failed in renderer, @@ -1596,12 +1601,12 @@ bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange( return false; DCHECK_EQ(composition_bounds_.size(), composition_range_.length()); - ui::Range ui_actual_range; + gfx::Range ui_actual_range; *rect = NSRectFromCGRect(GetFirstRectForCompositionRange( request_range_in_composition, &ui_actual_range).ToCGRect()); if (actual_range) { - *actual_range = ui::Range( + *actual_range = gfx::Range( composition_range_.start() + ui_actual_range.start(), composition_range_.start() + ui_actual_range.end()).ToNSRange(); } @@ -1787,6 +1792,10 @@ void RenderWidgetHostViewMac::GotSoftwareFrame() { // Also note that it is necessary that clearDrawable be called if // overlapping views are not allowed, e.g, for content shell. // http://crbug.com/178408 + // Disable screen updates so that the changes of flashes is minimized. + // http://crbug.com/279472 + if (!use_core_animation_) + [[cocoa_view_ window] disableScreenUpdatesUntilFlush]; if (allow_overlapping_views_) DestroyCompositedIOSurfaceAndLayer(kLeaveContextBoundToView); else @@ -1853,8 +1862,8 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { render_widget_host_->GetRoutingID(), background)); } -void RenderWidgetHostViewMac::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewMac::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (!GetBrowserAccessibilityManager()) { SetBrowserAccessibilityManager( new BrowserAccessibilityManagerMac( @@ -1862,7 +1871,7 @@ void RenderWidgetHostViewMac::OnAccessibilityNotifications( BrowserAccessibilityManagerMac::GetEmptyDocument(), NULL)); } - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } void RenderWidgetHostViewMac::SetTextInputActive(bool active) { @@ -2325,7 +2334,7 @@ void RenderWidgetHostViewMac::FrameSwapped() { if (textToBeInserted_.length() > ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) { widgetHost->ImeConfirmComposition( - textToBeInserted_, ui::Range::InvalidRange(), false); + textToBeInserted_, gfx::Range::InvalidRange(), false); textInserted = YES; } @@ -2342,7 +2351,7 @@ void RenderWidgetHostViewMac::FrameSwapped() { } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) { if (unmarkTextCalled_) { widgetHost->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); } else { widgetHost->ImeCancelComposition(); } @@ -3422,7 +3431,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; // called in keyEvent: method. if (!handlingKeyDown_) { renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); } else { unmarkTextCalled_ = YES; } @@ -3515,7 +3524,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; if (handlingKeyDown_) { textToBeInserted_.append(base::SysNSStringToUTF16(im_text)); } else { - ui::Range replacement_range(replacementRange); + gfx::Range replacement_range(replacementRange); renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( base::SysNSStringToUTF16(im_text), replacement_range, false); } @@ -3660,7 +3669,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; if (renderWidgetHostView_->render_widget_host_) renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); [self cancelComposition]; } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm index 0d3ee0a8aa0..ba92843ebf7 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm @@ -78,7 +78,7 @@ class RenderWidgetHostEditCommandCounter : public RenderWidgetHostImpl { RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id), + : RenderWidgetHostImpl(delegate, process, routing_id, false), edit_command_message_count_(0) { } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm index 3c91bc6abdf..7a152d18da8 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm @@ -101,7 +101,7 @@ class MockRenderWidgetHostImpl : public RenderWidgetHostImpl { MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id) { + : RenderWidgetHostImpl(delegate, process, routing_id, false) { } MOCK_METHOD0(Focus, void()); @@ -140,7 +140,7 @@ void GenerateCompositionRectArray(const gfx::Point& origin, gfx::Rect GetExpectedRect(const gfx::Point& origin, const gfx::Size& size, - const ui::Range& range, + const gfx::Range& range, int line_no) { return gfx::Rect( origin.x() + range.start() * size.width(), @@ -264,7 +264,7 @@ TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) { new MockRenderProcessHost(&browser_context); // Owned by its |cocoa_view()|. RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl( - &delegate, process_host, MSG_ROUTING_NONE); + &delegate, process_host, MSG_ROUTING_NONE, false); RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>( RenderWidgetHostView::CreateViewForWidget(rwh)); @@ -298,7 +298,7 @@ TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) { new MockRenderProcessHost(&browser_context); // Owned by its |cocoa_view()|. RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl( - &delegate, process_host, MSG_ROUTING_NONE); + &delegate, process_host, MSG_ROUTING_NONE, false); RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>( RenderWidgetHostView::CreateViewForWidget(rwh)); @@ -322,7 +322,7 @@ TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) { const size_t kDummyOffset = 0; gfx::Rect caret_rect(10, 11, 0, 10); - ui::Range caret_range(0, 0); + gfx::Range caret_range(0, 0); ViewHostMsg_SelectionBounds_Params params; NSRect rect; @@ -336,24 +336,24 @@ TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) { &rect, &actual_range)); EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect))); - EXPECT_EQ(caret_range, ui::Range(actual_range)); + EXPECT_EQ(caret_range, gfx::Range(actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 1).ToNSRange(), + gfx::Range(0, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 3).ToNSRange(), + gfx::Range(2, 3).ToNSRange(), &rect, &actual_range)); // Caret moved. caret_rect = gfx::Rect(20, 11, 0, 10); - caret_range = ui::Range(1, 1); + caret_range = gfx::Range(1, 1); params.anchor_rect = params.focus_rect = caret_rect; rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range); rwhv_mac_->SelectionBoundsChanged(params); @@ -362,45 +362,45 @@ TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) { &rect, &actual_range)); EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect))); - EXPECT_EQ(caret_range, ui::Range(actual_range)); + EXPECT_EQ(caret_range, gfx::Range(actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 3).ToNSRange(), + gfx::Range(2, 3).ToNSRange(), &rect, &actual_range)); // No caret. - caret_range = ui::Range(1, 2); + caret_range = gfx::Range(1, 2); rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range); params.anchor_rect = caret_rect; params.focus_rect = gfx::Rect(30, 11, 0, 10); rwhv_mac_->SelectionBoundsChanged(params); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 1).ToNSRange(), + gfx::Range(0, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 2).ToNSRange(), + gfx::Range(2, 2).ToNSRange(), &rect, &actual_range)); } @@ -412,46 +412,46 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) { NSRect rect; // Make sure not crashing by passing NULL pointer instead of |actual_range|. EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, NULL)); // If there are no update from renderer, always returned caret position. NSRange actual_range; EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 1).ToNSRange(), + gfx::Range(0, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 0).ToNSRange(), + gfx::Range(1, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); // If the firstRectForCharacterRange is failed in renderer, empty rect vector // is sent. Make sure this does not crash. - rwhv_mac_->ImeCompositionRangeChanged(ui::Range(10, 12), + rwhv_mac_->ImeCompositionRangeChanged(gfx::Range(10, 12), std::vector<gfx::Rect>()); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(10, 11).ToNSRange(), + gfx::Range(10, 11).ToNSRange(), &rect, NULL)); const int kCompositionLength = 10; std::vector<gfx::Rect> composition_bounds; const int kCompositionStart = 3; - const ui::Range kCompositionRange(kCompositionStart, + const gfx::Range kCompositionRange(kCompositionStart, kCompositionStart + kCompositionLength); GenerateCompositionRectArray(kOrigin, kBoundsUnit, @@ -462,45 +462,45 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) { // Out of range requests will return caret position. EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 2).ToNSRange(), + gfx::Range(2, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(13, 14).ToNSRange(), + gfx::Range(13, 14).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(14, 15).ToNSRange(), + gfx::Range(14, 15).ToNSRange(), &rect, &actual_range)); for (int i = 0; i <= kCompositionLength; ++i) { for (int j = 0; j <= kCompositionLength - i; ++j) { - const ui::Range range(i, i + j); + const gfx::Range range(i, i + j); const gfx::Rect expected_rect = GetExpectedRect(kOrigin, kBoundsUnit, range, 0); - const NSRange request_range = ui::Range( + const NSRange request_range = gfx::Range( kCompositionStart + range.start(), kCompositionStart + range.end()).ToNSRange(); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange( request_range, &rect, &actual_range)); - EXPECT_EQ(ui::Range(request_range), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(request_range), gfx::Range(actual_range)); EXPECT_EQ(expected_rect, gfx::Rect(NSRectToCGRect(rect))); // Make sure not crashing by passing NULL pointer instead of @@ -520,7 +520,7 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) { const int kCompositionLength = 30; std::vector<gfx::Rect> composition_bounds; - const ui::Range kCompositionRange(0, kCompositionLength); + const gfx::Range kCompositionRange(0, kCompositionLength); // Set breaking point at 10 and 20. std::vector<size_t> break_points; break_points.push_back(10); @@ -533,113 +533,113 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) { rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds); // Range doesn't contain line breaking point. - ui::Range range; - range = ui::Range(5, 8); + gfx::Range range; + range = gfx::Range(5, 8); NSRange actual_range; EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(range, ui::Range(actual_range)); + EXPECT_EQ(range, gfx::Range(actual_range)); EXPECT_EQ( GetExpectedRect(kOrigin, kBoundsUnit, range, 0), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(15, 18); + range = gfx::Range(15, 18); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(range, ui::Range(actual_range)); + EXPECT_EQ(range, gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 8), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 1), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(25, 28); + range = gfx::Range(25, 28); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(range, ui::Range(actual_range)); + EXPECT_EQ(range, gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 8), 2), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 2), gfx::Rect(NSRectToCGRect(rect))); // Range contains line breaking point. - range = ui::Range(8, 12); + range = gfx::Range(8, 12); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(8, 10), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(8, 10), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(8, 10), 0), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 0), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(18, 22); + range = gfx::Range(18, 22); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(18, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(18, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(8, 10), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 1), gfx::Rect(NSRectToCGRect(rect))); // Start point is line breaking point. - range = ui::Range(10, 12); + range = gfx::Range(10, 12); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(10, 12), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(10, 12), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 2), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 1), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(20, 22); + range = gfx::Range(20, 22); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(20, 22), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(20, 22), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 2), 2), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 2), gfx::Rect(NSRectToCGRect(rect))); // End point is line breaking point. - range = ui::Range(5, 10); + range = gfx::Range(5, 10); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(5, 10), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(5, 10), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 10), 0), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 0), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(15, 20); + range = gfx::Range(15, 20); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(15, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(15, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 10), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 1), gfx::Rect(NSRectToCGRect(rect))); // Start and end point are same line breaking point. - range = ui::Range(10, 10); + range = gfx::Range(10, 10); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(10, 10), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(10, 10), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 0), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 1), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(20, 20); + range = gfx::Range(20, 20); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(20, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(20, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 0), 2), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 2), gfx::Rect(NSRectToCGRect(rect))); // Start and end point are different line breaking point. - range = ui::Range(10, 20); + range = gfx::Range(10, 20); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(10, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(10, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 10), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 10), 1), gfx::Rect(NSRectToCGRect(rect))); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win.cc b/chromium/content/browser/renderer_host/render_widget_host_view_win.cc index a6ee28b0189..de2aa656a0d 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_win.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_win.cc @@ -12,8 +12,9 @@ #include <map> #include <stack> +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/i18n/rtl.h" @@ -56,25 +57,26 @@ #include "third_party/WebKit/public/web/WebCompositionUnderline.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/skia/include/core/SkRegion.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/ime/composition_text.h" #include "ui/base/ime/win/imm32_manager.h" #include "ui/base/ime/win/tsf_input_scope.h" #include "ui/base/l10n/l10n_util_win.h" -#include "ui/base/text/text_elider.h" +#include "ui/base/sequential_id_generator.h" #include "ui/base/touch/touch_device.h" #include "ui/base/touch/touch_enabled.h" #include "ui/base/ui_base_switches.h" #include "ui/base/view_prop.h" -#include "ui/base/win/dpi.h" -#include "ui/base/win/hwnd_util.h" #include "ui/base/win/mouse_wheel_util.h" #include "ui/base/win/touch_input.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/canvas.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/screen.h" +#include "ui/gfx/text_elider.h" +#include "ui/gfx/win/dpi.h" +#include "ui/gfx/win/hwnd_util.h" #include "webkit/common/cursors/webcursor.h" #include "win8/util/win8_util.h" @@ -312,7 +314,7 @@ void GetScreenInfoForWindow(gfx::NativeViewId id, MONITORINFOEX monitor_info; monitor_info.cbSize = sizeof(MONITORINFOEX); - if (!GetMonitorInfo(monitor, &monitor_info)) + if (!base::win::GetMonitorInfoWrapper(monitor, &monitor_info)) return; DEVMODE dev_mode; @@ -323,7 +325,7 @@ void GetScreenInfoForWindow(gfx::NativeViewId id, WebKit::WebScreenInfo screen_info; screen_info.depth = dev_mode.dmBitsPerPel; screen_info.depthPerComponent = dev_mode.dmBitsPerPel / 3; // Assumes RGB - screen_info.deviceScaleFactor = ui::win::GetDeviceScaleFactor(); + screen_info.deviceScaleFactor = gfx::win::GetDeviceScaleFactor(); screen_info.isMonochrome = dev_mode.dmColor == DMCOLOR_MONOCHROME; screen_info.rect = gfx::Rect(monitor_info.rcMonitor); screen_info.availableRect = gfx::Rect(monitor_info.rcWork); @@ -355,8 +357,6 @@ class WebTouchState { bool is_changed() { return touch_event_.changedTouchesLength != 0; } private: - typedef std::map<unsigned int, int> MapType; - // Adds a touch point or returns NULL if there's not enough space. WebKit::WebTouchPoint* AddTouchPoint(TOUCHINPUT* touch_input); @@ -374,9 +374,7 @@ class WebTouchState { WebKit::WebTouchEvent touch_event_; const RenderWidgetHostViewWin* const window_; - // Maps OS touch Id's into an internal (WebKit-friendly) touch-id. - // WebKit expects small consecutive integers, starting at 0. - MapType touch_map_; + ui::SequentialIDGenerator id_generator_; DISALLOW_COPY_AND_ASSIGN(WebTouchState); }; @@ -395,7 +393,7 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) imm32_manager_(new ui::IMM32Manager), ime_notification_(false), capture_enter_key_(false), - is_hidden_(false), + is_hidden_(render_widget_host_->is_hidden()), about_to_validate_and_paint_(false), close_on_deactivate_(false), being_destroyed_(false), @@ -408,7 +406,7 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) can_compose_inline_(true), is_fullscreen_(false), ignore_mouse_movement_(true), - composition_range_(ui::Range::InvalidRange()), + composition_range_(gfx::Range::InvalidRange()), touch_state_(new WebTouchState(this)), pointer_down_context_(false), last_touch_location_(-1, -1), @@ -450,7 +448,7 @@ void RenderWidgetHostViewWin::InitAsFullscreen( gfx::Rect pos = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow( reference_host_view->GetNativeView()).bounds(); is_fullscreen_ = true; - DoPopupOrFullscreenInit(ui::GetWindowToParentTo(true), pos, 0); + DoPopupOrFullscreenInit(gfx::GetWindowToParentTo(true), pos, 0); } RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const { @@ -501,7 +499,7 @@ void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) { } void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) { - if (is_hidden_) + if (being_destroyed_) return; // No SWP_NOREDRAW as autofill popups can move and the underneath window @@ -574,7 +572,7 @@ void RenderWidgetHostViewWin::CleanupCompositorWindow() { if (!compositor_host_window_) return; - ui::SetWindowUserData(compositor_host_window_, NULL); + gfx::SetWindowUserData(compositor_host_window_, NULL); // Hide the compositor and parent it to the desktop rather than destroying // it immediately. The GPU process has a grace period to stop accessing the @@ -625,7 +623,7 @@ void RenderWidgetHostViewWin::Show() { } void RenderWidgetHostViewWin::Hide() { - if (!is_fullscreen_ && GetParent() == ui::GetWindowToParentTo(true)) { + if (!is_fullscreen_ && GetParent() == gfx::GetWindowToParentTo(true)) { LOG(WARNING) << "Hide() called twice in a row: " << this << ":" << GetParent(); return; @@ -643,7 +641,7 @@ bool RenderWidgetHostViewWin::IsShowing() { } gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const { - return ui::win::ScreenToDIPRect(GetPixelBounds()); + return gfx::win::ScreenToDIPRect(GetPixelBounds()); } gfx::Rect RenderWidgetHostViewWin::GetPixelBounds() const { @@ -690,8 +688,8 @@ void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) { void RenderWidgetHostViewWin::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (text_input_type_ != type || text_input_mode_ != input_mode || can_compose_inline_ != can_compose_inline) { @@ -725,7 +723,7 @@ void RenderWidgetHostViewWin::ImeCancelComposition() { } void RenderWidgetHostViewWin::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { composition_range_ = range; composition_character_bounds_ = character_bounds; @@ -762,6 +760,7 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore( const gfx::Vector2d& scroll_delta, const std::vector<gfx::Rect>& copy_rects, const ui::LatencyInfo& latency_info) { + TRACE_EVENT0("content", "RenderWidgetHostViewWin::DidUpdateBackingStore"); software_latency_info_.MergeWith(latency_info); if (is_hidden_) return; @@ -772,7 +771,7 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore( // surprisingly, this ordering matters. for (size_t i = 0; i < copy_rects.size(); ++i) { - gfx::Rect pixel_rect = ui::win::DIPToScreenRect(copy_rects[i]); + gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(copy_rects[i]); // Damage might not be DIP aligned. pixel_rect.Inset(-1, -1); RECT bounds = pixel_rect.ToRECT(); @@ -780,11 +779,12 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore( } if (!scroll_rect.IsEmpty()) { - gfx::Rect pixel_rect = ui::win::DIPToScreenRect(scroll_rect); + TRACE_EVENT0("content", "ScrollWindowEx"); + gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(scroll_rect); // Damage might not be DIP aligned. pixel_rect.Inset(-1, -1); RECT clip_rect = pixel_rect.ToRECT(); - float scale = ui::win::GetDeviceScaleFactor(); + float scale = gfx::win::GetDeviceScaleFactor(); int dx = static_cast<int>(scale * scroll_delta.x()); int dy = static_cast<int>(scale * scroll_delta.y()); ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE); @@ -806,7 +806,7 @@ bool RenderWidgetHostViewWin::CanSubscribeFrame() const { void RenderWidgetHostViewWin::WillWmDestroy() { CleanupCompositorWindow(); - if (base::win::IsTSFAwareRequired() && GetFocus() == m_hWnd) + if (base::win::IsTSFAwareRequired()) ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); } @@ -843,7 +843,7 @@ void RenderWidgetHostViewWin::SetTooltipText(const string16& tooltip_text) { // accidentally DOS the user with a mega tooltip (since Windows doesn't seem // to do this itself). const string16 new_tooltip_text = - ui::TruncateString(tooltip_text, kMaxTooltipLength); + gfx::TruncateString(tooltip_text, kMaxTooltipLength); if (new_tooltip_text != tooltip_text_) { tooltip_text_ = new_tooltip_text; @@ -883,7 +883,7 @@ void RenderWidgetHostViewWin::CopyFromCompositingSurface( if (dst_size.IsEmpty() || src_subrect.IsEmpty()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); accelerated_surface_->AsyncCopyTo(src_subrect, dst_size, callback); } @@ -902,7 +902,7 @@ void RenderWidgetHostViewWin::CopyFromCompositingSurfaceToVideoFrame( if (src_subrect.IsEmpty()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); accelerated_surface_->AsyncCopyToVideoFrame(src_subrect, target, callback); } @@ -1018,7 +1018,7 @@ void RenderWidgetHostViewWin::InsertText(const string16& text) { } if (render_widget_host_) render_widget_host_->ImeConfirmComposition(text, - ui::Range::InvalidRange(), + gfx::Range::InvalidRange(), false); } @@ -1048,7 +1048,7 @@ ui::TextInputMode RenderWidgetHostViewWin::GetTextInputMode() const { NOTREACHED(); return ui::TEXT_INPUT_MODE_DEFAULT; } - return ui::TEXT_INPUT_MODE_DEFAULT; + return text_input_mode_; } bool RenderWidgetHostViewWin::CanComposeInline() const { @@ -1096,7 +1096,7 @@ bool RenderWidgetHostViewWin::HasCompositionText() { return false; } -bool RenderWidgetHostViewWin::GetTextRange(ui::Range* range) { +bool RenderWidgetHostViewWin::GetTextRange(gfx::Range* range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1106,7 +1106,7 @@ bool RenderWidgetHostViewWin::GetTextRange(ui::Range* range) { return false; } -bool RenderWidgetHostViewWin::GetCompositionTextRange(ui::Range* range) { +bool RenderWidgetHostViewWin::GetCompositionTextRange(gfx::Range* range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1116,7 +1116,7 @@ bool RenderWidgetHostViewWin::GetCompositionTextRange(ui::Range* range) { return false; } -bool RenderWidgetHostViewWin::GetSelectionRange(ui::Range* range) { +bool RenderWidgetHostViewWin::GetSelectionRange(gfx::Range* range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1126,7 +1126,7 @@ bool RenderWidgetHostViewWin::GetSelectionRange(ui::Range* range) { return false; } -bool RenderWidgetHostViewWin::SetSelectionRange(const ui::Range& range) { +bool RenderWidgetHostViewWin::SetSelectionRange(const gfx::Range& range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1136,7 +1136,7 @@ bool RenderWidgetHostViewWin::SetSelectionRange(const ui::Range& range) { return false; } -bool RenderWidgetHostViewWin::DeleteRange(const ui::Range& range) { +bool RenderWidgetHostViewWin::DeleteRange(const gfx::Range& range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1146,13 +1146,13 @@ bool RenderWidgetHostViewWin::DeleteRange(const ui::Range& range) { return false; } -bool RenderWidgetHostViewWin::GetTextFromRange(const ui::Range& range, +bool RenderWidgetHostViewWin::GetTextFromRange(const gfx::Range& range, string16* text) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; } - ui::Range selection_text_range(selection_text_offset_, + gfx::Range selection_text_range(selection_text_offset_, selection_text_offset_ + selection_text_.length()); if (!selection_text_range.Contains(range)) { text->clear(); @@ -1313,7 +1313,7 @@ void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) { if (backing_store) { gfx::Rect bitmap_rect(gfx::Point(), - ui::win::DIPToScreenSize(backing_store->size())); + gfx::win::DIPToScreenSize(backing_store->size())); bool manage_colors = BackingStoreWin::ColorManagementEnabled(); if (manage_colors) @@ -1667,7 +1667,7 @@ LRESULT RenderWidgetHostViewWin::OnImeComposition( ui::CompositionText composition; if (imm32_manager_->GetResult(m_hWnd, lparam, &composition.text)) { render_widget_host_->ImeConfirmComposition( - composition.text, ui::Range::InvalidRange(), false); + composition.text, gfx::Range::InvalidRange(), false); imm32_manager_->ResetComposition(m_hWnd); // Fall though and try reading the composition string. // Japanese IMEs send a message containing both GCS_RESULTSTR and @@ -1679,7 +1679,7 @@ LRESULT RenderWidgetHostViewWin::OnImeComposition( if (imm32_manager_->GetComposition(m_hWnd, lparam, &composition)) { // TODO(suzhe): due to a bug of webkit, we can't use selection range with // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788 - composition.selection = ui::Range(composition.selection.end()); + composition.selection = gfx::Range(composition.selection.end()); // TODO(suzhe): convert both renderer_host and renderer to use // ui::CompositionText. @@ -2001,7 +2001,7 @@ LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, if (render_widget_host_) { WebKit::WebMouseWheelEvent wheel_event = WebMouseWheelEventBuilder::Build(m_hWnd, message, wparam, lparam); - float scale = ui::win::GetDeviceScaleFactor(); + float scale = gfx::win::GetDeviceScaleFactor(); wheel_event.x /= scale; wheel_event.y /= scale; wheel_event.deltaX /= scale; @@ -2014,7 +2014,9 @@ LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, } WebTouchState::WebTouchState(const RenderWidgetHostViewWin* window) - : window_(window) { } + : window_(window), + id_generator_(0) { +} size_t WebTouchState::UpdateTouchPoints( TOUCHINPUT* points, size_t count) { @@ -2114,22 +2116,12 @@ size_t WebTouchState::UpdateTouchPoints( } void WebTouchState::RemoveExpiredMappings() { - WebTouchState::MapType new_map; - for (MapType::iterator it = touch_map_.begin(); - it != touch_map_.end(); - ++it) { - WebKit::WebTouchPoint* point = touch_event_.touches; - WebKit::WebTouchPoint* end = point + touch_event_.touchesLength; - while (point < end) { - if ((point->id == it->second) && - (point->state != WebKit::WebTouchPoint::StateReleased)) { - new_map.insert(*it); - break; - } - point++; - } + WebKit::WebTouchPoint* point = touch_event_.touches; + WebKit::WebTouchPoint* end = point + touch_event_.touchesLength; + for (; point < end; ++point) { + if (point->state == WebKit::WebTouchPoint::StateReleased) + id_generator_.ReleaseGeneratedID(point->id); } - touch_map_.swap(new_map); } @@ -2167,18 +2159,20 @@ bool WebTouchState::UpdateTouchPoint( WebKit::WebTouchPoint* touch_point, TOUCHINPUT* touch_input) { CPoint coordinates( - TOUCH_COORD_TO_PIXEL(touch_input->x) / ui::win::GetUndocumentedDPIScale(), - TOUCH_COORD_TO_PIXEL(touch_input->y) / ui::win::GetUndocumentedDPIScale()); + TOUCH_COORD_TO_PIXEL(touch_input->x) / + gfx::win::GetUndocumentedDPITouchScale(), + TOUCH_COORD_TO_PIXEL(touch_input->y) / + gfx::win::GetUndocumentedDPITouchScale()); int radius_x = 1; int radius_y = 1; if (touch_input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) { // Some touch drivers send a contact area of "-1", yet flag it as valid. radius_x = std::max(1, static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cxContact) / - ui::win::GetUndocumentedDPIScale())); + gfx::win::GetUndocumentedDPITouchScale())); radius_y = std::max(1, static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cyContact) / - ui::win::GetUndocumentedDPIScale())); + gfx::win::GetUndocumentedDPITouchScale())); } // Detect and exclude stationary moves. @@ -2194,7 +2188,7 @@ bool WebTouchState::UpdateTouchPoint( touch_point->screenPosition.x = coordinates.x; touch_point->screenPosition.y = coordinates.y; window_->ScreenToClient(&coordinates); - static float scale = ui::win::GetDeviceScaleFactor(); + static float scale = gfx::win::GetDeviceScaleFactor(); touch_point->position.x = coordinates.x / scale; touch_point->position.y = coordinates.y / scale; touch_point->radiusX = radius_x; @@ -2206,14 +2200,7 @@ bool WebTouchState::UpdateTouchPoint( // Find (or create) a mapping for _os_touch_id_. unsigned int WebTouchState::GetMappedTouch(unsigned int os_touch_id) { - MapType::iterator it = touch_map_.find(os_touch_id); - if (it != touch_map_.end()) - return it->second; - int next_value = 0; - for (it = touch_map_.begin(); it != touch_map_.end(); ++it) - next_value = std::max(next_value, it->second + 1); - touch_map_[os_touch_id] = next_value; - return next_value; + return id_generator_.GetGeneratedID(os_touch_id); } LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam, @@ -2241,8 +2228,10 @@ LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam, if (total == 1 && (points[0].dwFlags & TOUCHEVENTF_DOWN)) { pointer_down_context_ = true; last_touch_location_ = gfx::Point( - TOUCH_COORD_TO_PIXEL(points[0].x) / ui::win::GetUndocumentedDPIScale(), - TOUCH_COORD_TO_PIXEL(points[0].y) / ui::win::GetUndocumentedDPIScale()); + TOUCH_COORD_TO_PIXEL(points[0].x) / + gfx::win::GetUndocumentedDPITouchScale(), + TOUCH_COORD_TO_PIXEL(points[0].y) / + gfx::win::GetUndocumentedDPITouchScale()); } bool should_forward = render_widget_host_->ShouldForwardTouchEvent() && @@ -2311,7 +2300,7 @@ LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message, ::ScreenToClient(m_hWnd, &cursor_pos); HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); if (::IsWindow(child_window) && child_window != m_hWnd) { - if (ui::GetClassName(child_window) == kWrapperNativeWindowClassName) + if (gfx::GetClassName(child_window) == kWrapperNativeWindowClassName) child_window = ::GetWindow(child_window, GW_CHILD); ::SetFocus(child_window); @@ -2372,10 +2361,10 @@ LRESULT RenderWidgetHostViewWin::OnMoveOrSize( return 0; } -void RenderWidgetHostViewWin::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewWin::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { CreateBrowserAccessibilityManagerIfNeeded(); - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } bool RenderWidgetHostViewWin::LockMouse() { @@ -2457,7 +2446,7 @@ static void PaintCompositorHostWindow(HWND hWnd) { BeginPaint(hWnd, &paint); RenderWidgetHostViewWin* win = static_cast<RenderWidgetHostViewWin*>( - ui::GetWindowUserData(hWnd)); + gfx::GetWindowUserData(hWnd)); // Trigger composite to rerender window. if (win) win->AcceleratedPaint(paint.hdc); @@ -2506,7 +2495,7 @@ gfx::Rect RenderWidgetHostViewWin::GetBoundsInRootWindow() { GetSystemMetrics(SM_CYSIZEFRAME)); } - return ui::win::ScreenToDIPRect(rect); + return gfx::win::ScreenToDIPRect(rect); } // Creates a HWND within the RenderWidgetHostView that will serve as a host @@ -2556,9 +2545,9 @@ gfx::GLSurfaceHandle RenderWidgetHostViewWin::GetCompositingSurface() { MAKEINTATOM(atom), 0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED, 0, 0, width, height, m_hWnd, 0, instance, 0); - ui::CheckWindowCreated(compositor_host_window_); + gfx::CheckWindowCreated(compositor_host_window_); - ui::SetWindowUserData(compositor_host_window_, this); + gfx::SetWindowUserData(compositor_host_window_, this); gfx::GLSurfaceHandle surface_handle(compositor_host_window_, gfx::NATIVE_TRANSPORT); @@ -2754,6 +2743,8 @@ void RenderWidgetHostViewWin::OnFinalMessage(HWND window) { } if (render_widget_host_) render_widget_host_->ViewDestroyed(); + if (base::win::IsTSFAwareRequired()) + ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); delete this; } @@ -2887,7 +2878,7 @@ void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, return; } - gfx::Point point = ui::win::ScreenToDIPPoint( + gfx::Point point = gfx::win::ScreenToDIPPoint( gfx::Point(static_cast<short>(LOWORD(lparam)), static_cast<short>(HIWORD(lparam)))); lparam = MAKELPARAM(point.x(), point.y()); @@ -2966,7 +2957,7 @@ void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd, const gfx::Rect& pos, DWORD ex_style) { Create(parent_hwnd, NULL, NULL, WS_POPUP, ex_style); - gfx::Rect screen_rect = ui::win::DIPToScreenRect(pos); + gfx::Rect screen_rect = gfx::win::DIPToScreenRect(pos); MoveWindow(screen_rect.x(), screen_rect.y(), screen_rect.width(), screen_rect.height(), TRUE); ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA); @@ -3180,7 +3171,7 @@ void RenderWidgetHostViewWin::UpdateInputScopeIfNecessary( return; ui::tsf_inputscope::SetInputScopeForTsfUnawareWindow( - m_hWnd, text_input_type, ui::TEXT_INPUT_MODE_DEFAULT); + m_hWnd, text_input_type, text_input_mode_); } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win.h b/chromium/content/browser/renderer_host/render_widget_host_view_win.h index 52030d78f39..6650ae4ba8b 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_win.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_win.h @@ -182,14 +182,14 @@ class RenderWidgetHostViewWin virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -233,8 +233,8 @@ class RenderWidgetHostViewWin virtual void AcceleratedSurfaceSuspend() OVERRIDE; virtual void AcceleratedSurfaceRelease() OVERRIDE; virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params ) OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; @@ -277,12 +277,12 @@ class RenderWidgetHostViewWin virtual bool GetCompositionCharacterBounds(uint32 index, gfx::Rect* rect) OVERRIDE; virtual bool HasCompositionText() OVERRIDE; - virtual bool GetTextRange(ui::Range* range) OVERRIDE; - virtual bool GetCompositionTextRange(ui::Range* range) OVERRIDE; - virtual bool GetSelectionRange(ui::Range* range) OVERRIDE; - virtual bool SetSelectionRange(const ui::Range& range) OVERRIDE; - virtual bool DeleteRange(const ui::Range& range) OVERRIDE; - virtual bool GetTextFromRange(const ui::Range& range, + virtual bool GetTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetCompositionTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetSelectionRange(gfx::Range* range) OVERRIDE; + virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; + virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; + virtual bool GetTextFromRange(const gfx::Range& range, string16* text) OVERRIDE; virtual void OnInputMethodChanged() OVERRIDE; virtual bool ChangeTextDirectionAndLayoutAlignment( @@ -572,7 +572,7 @@ class RenderWidgetHostViewWin // back, we regard the mouse movement as (0, 0). bool ignore_mouse_movement_; - ui::Range composition_range_; + gfx::Range composition_range_; // The current composition character bounds. std::vector<gfx::Rect> composition_character_bounds_; diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc index 76070bc70e5..fc73601fb6c 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc @@ -10,11 +10,11 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" -#include "content/public/test/test_utils.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" -#include "content/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" +#include "content/test/content_browser_test_utils.h" #include "ui/base/ime/composition_text.h" #include "ui/base/ime/text_input_type.h" #include "ui/base/ime/win/imm32_manager.h" @@ -87,16 +87,16 @@ IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewWinBrowserTest, MockIMM32Manager* mock = new MockIMM32Manager(); mock->Reset(); view_->imm32_manager_.reset(mock); - view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, false, - ui::TEXT_INPUT_MODE_EMAIL); + view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, + ui::TEXT_INPUT_MODE_EMAIL, false); EXPECT_EQ(1, mock->call_count()); EXPECT_EQ(view_->m_hWnd, mock->window_handle()); EXPECT_EQ(ui::TEXT_INPUT_MODE_EMAIL, mock->input_mode()); mock->Reset(); - view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, false, - ui::TEXT_INPUT_MODE_EMAIL); + view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, + ui::TEXT_INPUT_MODE_EMAIL, false); EXPECT_EQ(0, mock->call_count()); } diff --git a/chromium/content/browser/renderer_host/smooth_scroll_calculator.cc b/chromium/content/browser/renderer_host/smooth_scroll_calculator.cc deleted file mode 100644 index 2538a188354..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_calculator.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2013 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/smooth_scroll_calculator.h" - -namespace content { - -SmoothScrollCalculator::SmoothScrollCalculator() { -} - -SmoothScrollCalculator::~SmoothScrollCalculator() { -} - -double SmoothScrollCalculator::GetScrollDelta( - base::TimeTicks now, base::TimeDelta desired_interval) { - double position_delta = 10; - if (!last_tick_time_.is_null()) { - double velocity = 10 / desired_interval.InMillisecondsF(); - double time_delta = (now - last_tick_time_).InMillisecondsF(); - position_delta = velocity * time_delta; - } - - last_tick_time_ = now; - return position_delta; -} - -} // namespace content diff --git a/chromium/content/browser/renderer_host/smooth_scroll_calculator.h b/chromium/content/browser/renderer_host/smooth_scroll_calculator.h deleted file mode 100644 index 1427416be41..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_calculator.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2013 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_SMOOTH_SCROLL_CALCULATOR_H_ -#define CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_ - -#include "base/time/time.h" - -namespace content { - -// An utility class to calculate the delta for smooth scroll gesture -// events. -class SmoothScrollCalculator { - public: - SmoothScrollCalculator(); - ~SmoothScrollCalculator(); - - double GetScrollDelta(base::TimeTicks now, base::TimeDelta desired_interval); - - private: - base::TimeTicks last_tick_time_; - - DISALLOW_COPY_AND_ASSIGN(SmoothScrollCalculator); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_ diff --git a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.cc b/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.cc deleted file mode 100644 index ca9dfae9b78..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2013 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/smooth_scroll_gesture_controller.h" - -#include "base/debug/trace_event.h" -#include "base/message_loop/message_loop.h" -#include "content/common/view_messages.h" -#include "content/port/browser/render_widget_host_view_port.h" -#include "content/port/browser/smooth_scroll_gesture.h" -#include "content/public/browser/render_widget_host.h" - -namespace content { - -namespace { - -// How many milliseconds apart synthetic scroll messages should be sent. -const int kSyntheticScrollMessageIntervalMs = 7; - -} // namespace - -SmoothScrollGestureController::SmoothScrollGestureController() - : rwh_(NULL) { -} - -SmoothScrollGestureController::~SmoothScrollGestureController() { -} - -void SmoothScrollGestureController::BeginSmoothScroll( - RenderWidgetHostViewPort* view, - const ViewHostMsg_BeginSmoothScroll_Params& params) { - if (pending_smooth_scroll_gesture_.get()) - return; - - rwh_ = view->GetRenderWidgetHost(); - pending_smooth_scroll_gesture_ = view->CreateSmoothScrollGesture( - params.scroll_down, - params.pixels_to_scroll, - params.mouse_event_x, - params.mouse_event_y); - - timer_.Start(FROM_HERE, GetSyntheticScrollMessageInterval(), this, - &SmoothScrollGestureController::OnTimer); -} - -base::TimeDelta - SmoothScrollGestureController::GetSyntheticScrollMessageInterval() const { - return base::TimeDelta::FromMilliseconds(kSyntheticScrollMessageIntervalMs); -} - -void SmoothScrollGestureController::OnTimer() { - base::TimeTicks now = base::TimeTicks::Now(); - if (!pending_smooth_scroll_gesture_->ForwardInputEvents(now, rwh_)) { - timer_.Stop(); - pending_smooth_scroll_gesture_ = NULL; - rwh_->Send(new ViewMsg_SmoothScrollCompleted(rwh_->GetRoutingID())); - } -} - -} // namespace content diff --git a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.h b/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.h deleted file mode 100644 index 1477a171e6a..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013 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_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_ -#define CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_ - -#include <map> - -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "base/timer/timer.h" -#include "content/common/content_export.h" - -struct ViewHostMsg_BeginSmoothScroll_Params; - -namespace content { - -class RenderWidgetHost; -class RenderWidgetHostViewPort; -class SmoothScrollGesture; - -// Controls SmoothScrollGestures, used to inject synthetic events -// for performance test harness. -class CONTENT_EXPORT SmoothScrollGestureController { - public: - SmoothScrollGestureController(); - ~SmoothScrollGestureController(); - - // Initiates a synthetic event stream. - void BeginSmoothScroll(RenderWidgetHostViewPort* view, - const ViewHostMsg_BeginSmoothScroll_Params& params); - - base::TimeDelta GetSyntheticScrollMessageInterval() const; - - private: - // Called periodically to advance the active scroll gesture after being - // initiated by OnBeginSmoothScroll. - void OnTimer(); - - base::RepeatingTimer<SmoothScrollGestureController> timer_; - - RenderWidgetHost* rwh_; - - scoped_refptr<SmoothScrollGesture> pending_smooth_scroll_gesture_; - - DISALLOW_COPY_AND_ASSIGN(SmoothScrollGestureController); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_ diff --git a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc index 7f122df2c73..677c281932c 100644 --- a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc +++ b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc @@ -30,14 +30,13 @@ const size_t kMaxSocketStreamHosts = 16 * 1024; SocketStreamDispatcherHost::SocketStreamDispatcherHost( int render_process_id, - ResourceMessageFilter::URLRequestContextSelector* selector, + const GetRequestContextCallback& request_context_callback, ResourceContext* resource_context) : render_process_id_(render_process_id), - url_request_context_selector_(selector), + request_context_callback_(request_context_callback), resource_context_(resource_context), weak_ptr_factory_(this), on_shutdown_(false) { - DCHECK(selector); net::WebSocketJob::EnsureInit(); } @@ -263,8 +262,7 @@ void SocketStreamDispatcherHost::DeleteSocketStreamHost(int socket_id) { } net::URLRequestContext* SocketStreamDispatcherHost::GetURLRequestContext() { - return url_request_context_selector_->GetRequestContext( - ResourceType::SUB_RESOURCE); + return request_context_callback_.Run(ResourceType::SUB_RESOURCE); } void SocketStreamDispatcherHost::Shutdown() { diff --git a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h index 732923e690f..958eada3a57 100644 --- a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h +++ b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h @@ -7,9 +7,9 @@ #include <vector> +#include "base/callback_forward.h" #include "base/id_map.h" #include "base/memory/weak_ptr.h" -#include "content/browser/loader/resource_message_filter.h" #include "content/browser/ssl/ssl_error_handler.h" #include "content/public/browser/browser_message_filter.h" #include "net/socket_stream/socket_stream.h" @@ -32,9 +32,11 @@ class SocketStreamDispatcherHost public net::SocketStream::Delegate, public SSLErrorHandler::Delegate { public: + typedef base::Callback<net::URLRequestContext*(ResourceType::Type)> + GetRequestContextCallback; SocketStreamDispatcherHost( int render_process_id, - ResourceMessageFilter::URLRequestContextSelector* selector, + const GetRequestContextCallback& request_context_callback, ResourceContext* resource_context); // BrowserMessageFilter: @@ -84,8 +86,7 @@ class SocketStreamDispatcherHost IDMap<SocketStreamHost> hosts_; int render_process_id_; - const scoped_ptr<ResourceMessageFilter::URLRequestContextSelector> - url_request_context_selector_; + GetRequestContextCallback request_context_callback_; ResourceContext* resource_context_; base::WeakPtrFactory<SocketStreamDispatcherHost> weak_ptr_factory_; diff --git a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc index b563480efd9..d382cd57565 100644 --- a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc +++ b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc @@ -15,7 +15,7 @@ #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" -#include "ui/gl/android/surface_texture_bridge.h" +#include "ui/gl/android/surface_texture.h" namespace content { @@ -26,7 +26,7 @@ static const uint32 kGLTextureExternalOES = 0x8D65; class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef { public: SurfaceRefAndroid( - const scoped_refptr<gfx::SurfaceTextureBridge>& surface, + const scoped_refptr<gfx::SurfaceTexture>& surface, ANativeWindow* window) : surface_(surface), window_(window) { @@ -39,7 +39,7 @@ class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef { ANativeWindow_release(window_); } - scoped_refptr<gfx::SurfaceTextureBridge> surface_; + scoped_refptr<gfx::SurfaceTexture> surface_; ANativeWindow* window_; }; @@ -61,7 +61,7 @@ scoped_refptr<cc::Layer> SurfaceTextureTransportClient::Initialize() { // Use a SurfaceTexture to stream frames to the UI thread. video_layer_ = cc::VideoLayer::Create(this); - surface_texture_ = new gfx::SurfaceTextureBridge(0); + surface_texture_ = new gfx::SurfaceTexture(0); surface_texture_->SetFrameAvailableCallback( base::Bind( &SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable, diff --git a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h index 83d917552dc..5a000bb246e 100644 --- a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h +++ b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h @@ -21,7 +21,7 @@ class VideoLayer; } namespace gfx { -class SurfaceTextureBridge; +class SurfaceTexture; } namespace content { @@ -45,7 +45,7 @@ class SurfaceTextureTransportClient : public cc::VideoFrameProvider { void OnSurfaceTextureFrameAvailable(); scoped_refptr<cc::VideoLayer> video_layer_; - scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_; + scoped_refptr<gfx::SurfaceTexture> surface_texture_; ANativeWindow* window_; scoped_refptr<media::VideoFrame> video_frame_; uint32 texture_id_; diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_calculator.cc b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.cc new file mode 100644 index 00000000000..9816df93b48 --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.cc @@ -0,0 +1,37 @@ +// Copyright 2013 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/synthetic_gesture_calculator.h" + + +namespace { + +const float kDefaultPositionDelta = 10.0f; + +} + + +namespace content { + +SyntheticGestureCalculator::SyntheticGestureCalculator() { +} + +SyntheticGestureCalculator::~SyntheticGestureCalculator() { +} + +float SyntheticGestureCalculator::GetDelta( + base::TimeTicks now, base::TimeDelta desired_interval) { + float position_delta = kDefaultPositionDelta; + if (!last_tick_time_.is_null()) { + float velocity = kDefaultPositionDelta / + (float)desired_interval.InMillisecondsF(); + float time_delta = (now - last_tick_time_).InMillisecondsF(); + position_delta = velocity * time_delta; + } + + last_tick_time_ = now; + return position_delta; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_calculator.h b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.h new file mode 100644 index 00000000000..c2af4978ac4 --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.h @@ -0,0 +1,28 @@ +// Copyright 2013 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_SYNTHETIC_GESTURE_CALCULATOR_H_ +#define CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_ + +#include "base/time/time.h" + +namespace content { + +// A utility class to calculate the delta for synthetic gesture events. +class SyntheticGestureCalculator { + public: + SyntheticGestureCalculator(); + ~SyntheticGestureCalculator(); + + float GetDelta(base::TimeTicks now, base::TimeDelta desired_interval); + + private: + base::TimeTicks last_tick_time_; + + DISALLOW_COPY_AND_ASSIGN(SyntheticGestureCalculator); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_ diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_controller.cc b/chromium/content/browser/renderer_host/synthetic_gesture_controller.cc new file mode 100644 index 00000000000..46c4329da6b --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_controller.cc @@ -0,0 +1,84 @@ +// Copyright 2013 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/synthetic_gesture_controller.h" + +#include "base/debug/trace_event.h" +#include "base/message_loop/message_loop.h" +#include "content/common/view_messages.h" +#include "content/port/browser/render_widget_host_view_port.h" +#include "content/port/browser/synthetic_gesture.h" +#include "content/public/browser/render_widget_host.h" + +namespace content { + +namespace { + +// How many milliseconds apart synthetic scroll messages should be sent. +const int kSyntheticGestureMessageIntervalMs = 7; + +} // namespace + +SyntheticGestureController::SyntheticGestureController() + : rwh_(NULL) { +} + +SyntheticGestureController::~SyntheticGestureController() { +} + +void SyntheticGestureController::BeginSmoothScroll( + RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginSmoothScroll_Params& params) { + if (pending_synthetic_gesture_.get()) + return; + + rwh_ = view->GetRenderWidgetHost(); + pending_synthetic_gesture_ = view->CreateSmoothScrollGesture( + params.scroll_down, + params.pixels_to_scroll, + params.mouse_event_x, + params.mouse_event_y); + + TRACE_EVENT_ASYNC_BEGIN0("benchmark", "SyntheticGestureController::running", + pending_synthetic_gesture_); + timer_.Start(FROM_HERE, GetSyntheticGestureMessageInterval(), this, + &SyntheticGestureController::OnTimer); +} + +void SyntheticGestureController::BeginPinch( + RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginPinch_Params& params) { + if (pending_synthetic_gesture_.get()) + return; + + rwh_ = view->GetRenderWidgetHost(); + pending_synthetic_gesture_ = view->CreatePinchGesture( + params.zoom_in, + params.pixels_to_move, + params.anchor_x, + params.anchor_y); + + TRACE_EVENT_ASYNC_BEGIN0("benchmark", "SyntheticGestureController::running", + pending_synthetic_gesture_); + timer_.Start(FROM_HERE, GetSyntheticGestureMessageInterval(), this, + &SyntheticGestureController::OnTimer); +} + +base::TimeDelta + SyntheticGestureController::GetSyntheticGestureMessageInterval() const { + return base::TimeDelta::FromMilliseconds(kSyntheticGestureMessageIntervalMs); +} + +void SyntheticGestureController::OnTimer() { + base::TimeTicks now = base::TimeTicks::Now(); + if (!pending_synthetic_gesture_->ForwardInputEvents(now, rwh_)) { + timer_.Stop(); + TRACE_EVENT_ASYNC_END0("benchmark", "SyntheticGestureController::running", + pending_synthetic_gesture_); + pending_synthetic_gesture_ = NULL; + rwh_->Send(new ViewMsg_SyntheticGestureCompleted(rwh_->GetRoutingID())); + } +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_controller.h b/chromium/content/browser/renderer_host/synthetic_gesture_controller.h new file mode 100644 index 00000000000..8826aaddf3a --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_controller.h @@ -0,0 +1,57 @@ +// Copyright 2013 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_SYNTHETIC_GESTURE_CONTROLLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_ + +#include <map> + +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "content/common/content_export.h" + +struct ViewHostMsg_BeginPinch_Params; +struct ViewHostMsg_BeginSmoothScroll_Params; + +namespace content { + +class RenderWidgetHost; +class RenderWidgetHostViewPort; +class SyntheticGesture; + +// Controls SyntheticGestures, used to inject synthetic events +// for performance test harness. +class CONTENT_EXPORT SyntheticGestureController { + public: + SyntheticGestureController(); + ~SyntheticGestureController(); + + // Initiates a synthetic event stream to simulate a smooth scroll. + void BeginSmoothScroll(RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginSmoothScroll_Params& params); + + // Initiates a synthetic event stream to simulate a pinch-to-zoom. + void BeginPinch(RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginPinch_Params& params); + + base::TimeDelta GetSyntheticGestureMessageInterval() const; + + private: + // Called periodically to advance the active scroll gesture after being + // initiated by OnBeginSmoothScroll. + void OnTimer(); + + base::RepeatingTimer<SyntheticGestureController> timer_; + + RenderWidgetHost* rwh_; + + scoped_refptr<SyntheticGesture> pending_synthetic_gesture_; + + DISALLOW_COPY_AND_ASSIGN(SyntheticGestureController); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_ diff --git a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc b/chromium/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc index 95d26473ee1..7fb9ba9e601 100644 --- a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc +++ b/chromium/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 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. @@ -6,11 +6,11 @@ #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" -#include "content/browser/renderer_host/smooth_scroll_gesture_controller.h" +#include "content/browser/renderer_host/synthetic_gesture_controller.h" #include "content/browser/renderer_host/test_render_view_host.h" #include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/port/browser/synthetic_gesture.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,9 +26,9 @@ namespace content { namespace { -class MockSmoothScrollGesture : public SmoothScrollGesture { +class MockSyntheticGesture : public SyntheticGesture { public: - MockSmoothScrollGesture() : + MockSyntheticGesture() : called_(0) { } @@ -42,7 +42,7 @@ class MockSmoothScrollGesture : public SmoothScrollGesture { int called_; protected: - virtual ~MockSmoothScrollGesture() { + virtual ~MockSyntheticGesture() { } }; @@ -59,7 +59,7 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl { RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id) { + : RenderWidgetHostImpl(delegate, process, routing_id, false) { } virtual ~MockRenderWidgetHost() {} }; @@ -73,10 +73,10 @@ class TestView : public TestRenderWidgetHostView { virtual ~TestView() {} // TestRenderWidgetHostView implementation: - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) OVERRIDE { - mock_gesture_ = new MockSmoothScrollGesture(); + mock_gesture_ = new MockSyntheticGesture(); return mock_gesture_; } @@ -84,14 +84,14 @@ class TestView : public TestRenderWidgetHostView { return rwh_; } - MockSmoothScrollGesture* mock_gesture_; + MockSyntheticGesture* mock_gesture_; }; -class SmoothScrollGestureControllerTest : public testing::Test { +class SyntheticGestureControllerTest : public testing::Test { public: - SmoothScrollGestureControllerTest() : process_(NULL) { + SyntheticGestureControllerTest() : process_(NULL) { } - virtual ~SmoothScrollGestureControllerTest() {} + virtual ~SyntheticGestureControllerTest() {} protected: // testing::Test implementation: @@ -131,7 +131,7 @@ class SmoothScrollGestureControllerTest : public testing::Test { base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds( - controller_.GetSyntheticScrollMessageInterval().InMilliseconds() * + controller_.GetSyntheticGestureMessageInterval().InMilliseconds() * 3)); base::MessageLoop::current()->Run(); } @@ -147,10 +147,10 @@ class SmoothScrollGestureControllerTest : public testing::Test { scoped_ptr<gfx::Screen> screen_; #endif - SmoothScrollGestureController controller_; + SyntheticGestureController controller_; }; -TEST_F(SmoothScrollGestureControllerTest, Tick) { +TEST_F(SyntheticGestureControllerTest, Tick) { ViewHostMsg_BeginSmoothScroll_Params params; params.scroll_down = true; params.pixels_to_scroll = 10; @@ -168,7 +168,7 @@ TEST_F(SmoothScrollGestureControllerTest, Tick) { EXPECT_LT(0, current_ticks); // Ensure it won't start another smooth scroll. - MockSmoothScrollGesture* original_gesture = view_->mock_gesture_; + MockSyntheticGesture* original_gesture = view_->mock_gesture_; controller_.BeginSmoothScroll(view_.get(), params); PostQuitMessageAndRun(); EXPECT_EQ(original_gesture, view_->mock_gesture_); diff --git a/chromium/content/browser/renderer_host/test_render_view_host.cc b/chromium/content/browser/renderer_host/test_render_view_host.cc index 129591cac7b..caf79cf8583 100644 --- a/chromium/content/browser/renderer_host/test_render_view_host.cc +++ b/chromium/content/browser/renderer_host/test_render_view_host.cc @@ -15,7 +15,6 @@ #include "content/public/browser/storage_partition.h" #include "content/public/common/content_client.h" #include "content/public/common/page_state.h" -#include "content/public/common/password_form.h" #include "content/test/test_web_contents.h" #include "media/base/video_frame.h" #include "ui/gfx/rect.h" @@ -42,7 +41,6 @@ void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params, params->should_update_history = false; params->searchable_form_url = GURL(); params->searchable_form_encoding = std::string(); - params->password_form = PasswordForm(); params->security_info = std::string(); params->gesture = NavigationGestureUser; params->was_within_same_page = false; @@ -214,14 +212,6 @@ void TestRenderWidgetHostView::SetClickthroughRegion(SkRegion* region) { } #endif -#if defined(OS_WIN) && defined(USE_AURA) -gfx::NativeViewAccessible -TestRenderWidgetHostView::AccessibleObjectFromChildId(long child_id) { - NOTIMPLEMENTED(); - return NULL; -} -#endif - bool TestRenderWidgetHostView::LockMouse() { return false; } @@ -247,7 +237,8 @@ TestRenderViewHost::TestRenderViewHost( widget_delegate, routing_id, main_frame_routing_id, - swapped_out), + swapped_out, + false /* hidden */), render_view_created_(false), delete_counter_(NULL), simulate_fetch_via_proxy_(false), @@ -333,7 +324,6 @@ void TestRenderViewHost::SendNavigateWithParameters( params.should_update_history = true; params.searchable_form_url = GURL(); params.searchable_form_encoding = std::string(); - params.password_form = PasswordForm(); params.security_info = std::string(); params.gesture = NavigationGestureUser; params.contents_mime_type = contents_mime_type_; diff --git a/chromium/content/browser/renderer_host/test_render_view_host.h b/chromium/content/browser/renderer_host/test_render_view_host.h index 37b46e2bef8..1eb7583bec0 100644 --- a/chromium/content/browser/renderer_host/test_render_view_host.h +++ b/chromium/content/browser/renderer_host/test_render_view_host.h @@ -99,12 +99,12 @@ class TestRenderWidgetHostView : public RenderWidgetHostViewBase { virtual void SetIsLoading(bool is_loading) OVERRIDE {} virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE {} virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE {} + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE {} virtual void ImeCancelComposition() OVERRIDE {} #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE {} #endif virtual void DidUpdateBackingStore( @@ -158,17 +158,12 @@ class TestRenderWidgetHostView : public RenderWidgetHostViewBase { bool has_horizontal_scrollbar) OVERRIDE { } virtual void SetScrollOffsetPinning( bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE { } - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& - params) OVERRIDE {} + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE {} virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; #if defined(OS_WIN) && !defined(USE_AURA) virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE; #endif -#if defined(OS_WIN) && defined(USE_AURA) - virtual gfx::NativeViewAccessible AccessibleObjectFromChildId(long child_id) - OVERRIDE; -#endif virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; #if defined(OS_WIN) && defined(USE_AURA) diff --git a/chromium/content/browser/renderer_host/text_input_client_mac.mm b/chromium/content/browser/renderer_host/text_input_client_mac.mm index cdf56a1eb19..b9d900a704e 100644 --- a/chromium/content/browser/renderer_host/text_input_client_mac.mm +++ b/chromium/content/browser/renderer_host/text_input_client_mac.mm @@ -61,7 +61,7 @@ NSRect TextInputClientMac::GetFirstRectForRange(RenderWidgetHost* rwh, RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh); rwhi->Send( new TextInputClientMsg_FirstRectForCharacterRange(rwhi->GetRoutingID(), - ui::Range(range))); + gfx::Range(range))); // http://crbug.com/121917 base::ThreadRestrictions::ScopedAllowWait allow_wait; condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout)); @@ -82,7 +82,7 @@ NSAttributedString* TextInputClientMac::GetAttributedSubstringFromRange( BeforeRequest(); RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh); rwhi->Send(new TextInputClientMsg_StringForRange(rwhi->GetRoutingID(), - ui::Range(range))); + gfx::Range(range))); // http://crbug.com/121917 base::ThreadRestrictions::ScopedAllowWait allow_wait; condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout)); diff --git a/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm b/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm index 2a4c6d7f08d..0993018e8f4 100644 --- a/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm +++ b/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm @@ -42,7 +42,7 @@ class TextInputClientMacTest : public testing::Test { widget_(&delegate_, process_factory_.CreateRenderProcessHost( &browser_context_, NULL), - MSG_ROUTING_NONE), + MSG_ROUTING_NONE, false), thread_("TextInputClientMacTestThread") {} // Accessor for the TextInputClientMac instance. diff --git a/chromium/content/browser/renderer_host/text_input_client_message_filter.h b/chromium/content/browser/renderer_host/text_input_client_message_filter.h index 2a374df5c5e..5a1405ae5ff 100644 --- a/chromium/content/browser/renderer_host/text_input_client_message_filter.h +++ b/chromium/content/browser/renderer_host/text_input_client_message_filter.h @@ -9,11 +9,8 @@ #include "content/public/browser/browser_message_filter.h" namespace gfx { -class Rect; -} - -namespace ui { class Range; +class Rect; } namespace content { diff --git a/chromium/content/browser/renderer_host/text_input_client_message_filter.mm b/chromium/content/browser/renderer_host/text_input_client_message_filter.mm index cea0a347d70..7af5a8ff2bd 100644 --- a/chromium/content/browser/renderer_host/text_input_client_message_filter.mm +++ b/chromium/content/browser/renderer_host/text_input_client_message_filter.mm @@ -10,7 +10,7 @@ #include "content/common/text_input_client_messages.h" #include "content/public/browser/render_widget_host_view.h" #include "ipc/ipc_message_macros.h" -#include "ui/base/range/range.h" +#include "ui/gfx/range/range.h" #include "ui/gfx/rect.h" namespace content { diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc deleted file mode 100644 index c504f6c07c9..00000000000 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2013 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/touch_smooth_scroll_gesture_android.h" - -#include "base/debug/trace_event.h" -#include "content/browser/renderer_host/render_widget_host_impl.h" -#include "jni/SmoothScroller_jni.h" - -namespace { -bool g_jni_initialized = false; - -void RegisterNativesIfNeeded(JNIEnv* env) { - if (!g_jni_initialized) { - content::RegisterNativesImpl(env); - g_jni_initialized = true; - } -} -} // namespace - -namespace content { - -TouchSmoothScrollGestureAndroid::TouchSmoothScrollGestureAndroid( - int pixels_to_scroll, - RenderWidgetHost* rwh, - base::android::ScopedJavaLocalRef<jobject> java_scroller) - : pixels_scrolled_(0), - has_started_(false), - has_sent_motion_up_(false), - pixels_to_scroll_(pixels_to_scroll), - rwh_(rwh), - java_scroller_(java_scroller) { - JNIEnv* env = base::android::AttachCurrentThread(); - RegisterNativesIfNeeded(env); -} - -TouchSmoothScrollGestureAndroid::~TouchSmoothScrollGestureAndroid() { -} - -bool TouchSmoothScrollGestureAndroid::ForwardInputEvents( - base::TimeTicks now, RenderWidgetHost* host) { - if (!has_started_) { - has_started_ = true; - JNIEnv* env = base::android::AttachCurrentThread(); - Java_SmoothScroller_start( - env, java_scroller_.obj(), reinterpret_cast<int>(this)); - } - - TRACE_COUNTER_ID1( - "gpu", "smooth_scroll_by_pixels_scrolled", this, pixels_scrolled_); - - return !has_sent_motion_up_; -} - -double TouchSmoothScrollGestureAndroid::GetScrollDelta( - JNIEnv* env, jobject obj, double scale) { - double delta = smooth_scroll_calculator_.GetScrollDelta( - base::TimeTicks::Now(), - RenderWidgetHostImpl::From(rwh_)->GetSyntheticScrollMessageInterval()) - * scale; - pixels_scrolled_ += delta; - return delta; -} - -bool TouchSmoothScrollGestureAndroid::HasFinished(JNIEnv* env, jobject obj) { - return pixels_scrolled_ >= pixels_to_scroll_; -} - -void TouchSmoothScrollGestureAndroid::SetHasSentMotionUp( - JNIEnv* env, jobject obj) { - has_sent_motion_up_ = true; -} - -} // namespace content diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h deleted file mode 100644 index 0b892670643..00000000000 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013 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_TOUCH_SMOOTH_GESTURE_ANDROID_H_ -#define CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_ - -#include "base/android/jni_android.h" -#include "base/time/time.h" -#include "content/browser/renderer_host/smooth_scroll_calculator.h" -#include "content/port/browser/smooth_scroll_gesture.h" - -namespace content { - -class ContentViewCore; -class RenderWidgetHost; - -class TouchSmoothScrollGestureAndroid : public SmoothScrollGesture { - public: - TouchSmoothScrollGestureAndroid( - int pixels_to_scroll, - RenderWidgetHost* rwh, - base::android::ScopedJavaLocalRef<jobject> java_scroller); - - // Called by the java side once the TimeAnimator ticks. - double GetScrollDelta(JNIEnv* env, jobject obj, double scale); - bool HasFinished(JNIEnv* env, jobject obj); - void SetHasSentMotionUp(JNIEnv* env, jobject obj); - - // SmoothScrollGesture - virtual bool ForwardInputEvents(base::TimeTicks now, - RenderWidgetHost* host) OVERRIDE; - - private: - virtual ~TouchSmoothScrollGestureAndroid(); - - SmoothScrollCalculator smooth_scroll_calculator_; - - int pixels_scrolled_; - bool has_started_; - bool has_sent_motion_up_; - - int pixels_to_scroll_; - RenderWidgetHost* rwh_; - base::android::ScopedJavaGlobalRef<jobject> java_scroller_; - - DISALLOW_COPY_AND_ASSIGN(TouchSmoothScrollGestureAndroid); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_ diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc index ffb1564ded1..5df932dfa3b 100644 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc +++ b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc @@ -6,8 +6,8 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "ui/aura/root_window.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/transform.h" namespace { @@ -53,8 +53,8 @@ bool TouchSmoothScrollGestureAura::ForwardInputEvents( return false; RenderWidgetHostImpl* host_impl = RenderWidgetHostImpl::From(host); - double position_delta = smooth_scroll_calculator_.GetScrollDelta(now, - host_impl->GetSyntheticScrollMessageInterval()); + float position_delta = synthetic_gesture_calculator_.GetDelta(now, + host_impl->GetSyntheticGestureMessageInterval()); if (pixels_scrolled_ == 0) { InjectTouchEvent(location_, ui::ET_TOUCH_PRESSED, window_); diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h index 779e02d6dd0..890b1467d68 100644 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h +++ b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h @@ -6,8 +6,8 @@ #define CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_SCROLL_GESTURE_ #include "base/time/time.h" -#include "content/browser/renderer_host/smooth_scroll_calculator.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/browser/renderer_host/synthetic_gesture_calculator.h" +#include "content/port/browser/synthetic_gesture.h" #include "ui/gfx/point.h" namespace aura { @@ -16,7 +16,7 @@ class Window; namespace content { -class TouchSmoothScrollGestureAura : public SmoothScrollGesture { +class TouchSmoothScrollGestureAura : public SyntheticGesture { public: TouchSmoothScrollGestureAura(bool scroll_down, int pixels_to_scroll, @@ -35,7 +35,7 @@ class TouchSmoothScrollGestureAura : public SmoothScrollGesture { int pixels_scrolled_; gfx::Point location_; aura::Window* window_; - SmoothScrollCalculator smooth_scroll_calculator_; + SyntheticGestureCalculator synthetic_gesture_calculator_; DISALLOW_COPY_AND_ASSIGN(TouchSmoothScrollGestureAura); }; diff --git a/chromium/content/browser/renderer_host/ui_events_helper.cc b/chromium/content/browser/renderer_host/ui_events_helper.cc index 89242946cab..78a7bff4f74 100644 --- a/chromium/content/browser/renderer_host/ui_events_helper.cc +++ b/chromium/content/browser/renderer_host/ui_events_helper.cc @@ -5,8 +5,8 @@ #include "content/browser/renderer_host/ui_events_helper.h" #include "third_party/WebKit/public/web/WebInputEvent.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_constants.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" namespace { diff --git a/chromium/content/browser/renderer_host/web_input_event_aura.cc b/chromium/content/browser/renderer_host/web_input_event_aura.cc index 8931f1f5d91..6bb5e5838af 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aura.cc +++ b/chromium/content/browser/renderer_host/web_input_event_aura.cc @@ -6,11 +6,57 @@ #include "content/browser/renderer_host/ui_events_helper.h" #include "ui/aura/window.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" namespace content { +#if defined(USE_X11) || defined(USE_OZONE) +// From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp: +WebKit::WebUChar GetControlCharacter(int windows_key_code, bool shift) { + if (windows_key_code >= ui::VKEY_A && + windows_key_code <= ui::VKEY_Z) { + // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A + return windows_key_code - ui::VKEY_A + 1; + } + if (shift) { + // following graphics chars require shift key to input. + switch (windows_key_code) { + // ctrl-@ maps to \x00 (Null byte) + case ui::VKEY_2: + return 0; + // ctrl-^ maps to \x1E (Record separator, Information separator two) + case ui::VKEY_6: + return 0x1E; + // ctrl-_ maps to \x1F (Unit separator, Information separator one) + case ui::VKEY_OEM_MINUS: + return 0x1F; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + break; + } + } else { + switch (windows_key_code) { + // ctrl-[ maps to \x1B (Escape) + case ui::VKEY_OEM_4: + return 0x1B; + // ctrl-\ maps to \x1C (File separator, Information separator four) + case ui::VKEY_OEM_5: + return 0x1C; + // ctrl-] maps to \x1D (Group separator, Information separator three) + case ui::VKEY_OEM_6: + return 0x1D; + // ctrl-Enter maps to \x0A (Line feed) + case ui::VKEY_RETURN: + return 0x0A; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + break; + } + } + return 0; +} +#endif #if defined(OS_WIN) WebKit::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent( base::NativeEvent native_event); @@ -21,28 +67,104 @@ WebKit::WebKeyboardEvent MakeWebKeyboardEventFromNativeEvent( WebKit::WebGestureEvent MakeWebGestureEventFromNativeEvent( base::NativeEvent native_event); #elif defined(USE_X11) -WebKit::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent( - ui::ScrollEvent* event); WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( ui::KeyEvent* event); -WebKit::WebGestureEvent MakeWebGestureEventFromAuraEvent( - ui::ScrollEvent* event); -#else +#elif defined(USE_OZONE) +WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( + ui::KeyEvent* event) { + base::NativeEvent native_event = event->native_event(); + ui::EventType type = ui::EventTypeFromNative(native_event); + WebKit::WebKeyboardEvent webkit_event; + + webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); + webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); + + switch (type) { + case ui::ET_KEY_PRESSED: + webkit_event.type = event->is_char() ? WebKit::WebInputEvent::Char : + WebKit::WebInputEvent::RawKeyDown; + break; + case ui::ET_KEY_RELEASED: + webkit_event.type = WebKit::WebInputEvent::KeyUp; + break; + default: + NOTREACHED(); + } + + if (webkit_event.modifiers & WebKit::WebInputEvent::AltKey) + webkit_event.isSystemKey = true; + + wchar_t character = ui::KeyboardCodeFromNative(native_event); + webkit_event.windowsKeyCode = character; + webkit_event.nativeKeyCode = character; + + if (webkit_event.windowsKeyCode == ui::VKEY_RETURN) + webkit_event.unmodifiedText[0] = '\r'; + else + webkit_event.unmodifiedText[0] = character; + + if (webkit_event.modifiers & WebKit::WebInputEvent::ControlKey) { + webkit_event.text[0] = + GetControlCharacter( + webkit_event.windowsKeyCode, + webkit_event.modifiers & WebKit::WebInputEvent::ShiftKey); + } else { + webkit_event.text[0] = webkit_event.unmodifiedText[0]; + } + + webkit_event.setKeyIdentifierFromWindowsKeyCode(); + + return webkit_event; +} +#endif +#if defined(USE_X11) || defined(USE_OZONE) WebKit::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent( ui::ScrollEvent* event) { WebKit::WebMouseWheelEvent webkit_event; - return webkit_event; -} -WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( - ui::KeyEvent* event) { - WebKit::WebKeyboardEvent webkit_event; + webkit_event.type = WebKit::WebInputEvent::MouseWheel; + webkit_event.button = WebKit::WebMouseEvent::ButtonNone; + webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); + webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); + webkit_event.hasPreciseScrollingDeltas = true; + webkit_event.deltaX = event->x_offset(); + if (event->x_offset_ordinal() != 0.f && event->x_offset() != 0.f) { + webkit_event.accelerationRatioX = + event->x_offset_ordinal() / event->x_offset(); + } + webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick; + webkit_event.deltaY = event->y_offset(); + webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick; + if (event->y_offset_ordinal() != 0.f && event->y_offset() != 0.f) { + webkit_event.accelerationRatioY = + event->y_offset_ordinal() / event->y_offset(); + } return webkit_event; } WebKit::WebGestureEvent MakeWebGestureEventFromAuraEvent( ui::ScrollEvent* event) { WebKit::WebGestureEvent webkit_event; + + switch (event->type()) { + case ui::ET_SCROLL_FLING_START: + webkit_event.type = WebKit::WebInputEvent::GestureFlingStart; + webkit_event.data.flingStart.velocityX = event->x_offset(); + webkit_event.data.flingStart.velocityY = event->y_offset(); + break; + case ui::ET_SCROLL_FLING_CANCEL: + webkit_event.type = WebKit::WebInputEvent::GestureFlingCancel; + break; + case ui::ET_SCROLL: + NOTREACHED() << "Invalid gesture type: " << event->type(); + break; + default: + NOTREACHED() << "Unknown gesture type: " << event->type(); + } + + webkit_event.sourceDevice = WebKit::WebGestureEvent::Touchpad; + webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); + webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); return webkit_event; } @@ -234,6 +356,7 @@ WebKit::WebMouseEvent MakeWebMouseEventFromAuraEvent(ui::MouseEvent* event) { break; case ui::ET_MOUSE_RELEASED: webkit_event.type = WebKit::WebInputEvent::MouseUp; + webkit_event.clickCount = event->GetClickCount(); break; case ui::ET_MOUSE_ENTERED: case ui::ET_MOUSE_EXITED: diff --git a/chromium/content/browser/renderer_host/web_input_event_aura.h b/chromium/content/browser/renderer_host/web_input_event_aura.h index ddc59e0c46f..3c4c8945bed 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aura.h +++ b/chromium/content/browser/renderer_host/web_input_event_aura.h @@ -22,6 +22,10 @@ namespace content { // Used for scrolling. This matches Firefox behavior. const int kPixelsPerTick = 53; +#if defined(USE_X11) || defined(USE_OZONE) +CONTENT_EXPORT WebKit::WebUChar GetControlCharacter( + int windows_key_code, bool shift); +#endif CONTENT_EXPORT WebKit::WebMouseEvent MakeWebMouseEvent( ui::MouseEvent* event); CONTENT_EXPORT WebKit::WebMouseWheelEvent MakeWebMouseWheelEvent( diff --git a/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc b/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc index 0443c30dad9..818250c0dd6 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc +++ b/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc @@ -6,11 +6,12 @@ #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" #if defined(USE_X11) #include <X11/keysym.h> #include <X11/Xlib.h> +#include "ui/gfx/x/x11_types.h" #include "ui/base/x/x11_util.h" #endif @@ -56,7 +57,7 @@ TEST(WebInputEventAuraTest, TestMakeWebKeyboardEventWindowsKeyCode) { ui::VKEY_CONTROL, 0, // X does not set ControlMask for KeyPress. &xev); - xev.xkey.keycode = XKeysymToKeycode(ui::GetXDisplay(), XK_Control_L); + xev.xkey.keycode = XKeysymToKeycode(gfx::GetXDisplay(), XK_Control_L); ui::KeyEvent event(&xev, false /* is_char */); WebKit::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(&event); // ui::VKEY_LCONTROL, instead of ui::VKEY_CONTROL, should be filled. @@ -68,7 +69,7 @@ TEST(WebInputEventAuraTest, TestMakeWebKeyboardEventWindowsKeyCode) { ui::VKEY_CONTROL, 0, // X does not set ControlMask for KeyPress. &xev); - xev.xkey.keycode = XKeysymToKeycode(ui::GetXDisplay(), XK_Control_R); + xev.xkey.keycode = XKeysymToKeycode(gfx::GetXDisplay(), XK_Control_R); ui::KeyEvent event(&xev, false /* is_char */); WebKit::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(&event); // ui::VKEY_RCONTROL, instead of ui::VKEY_CONTROL, should be filled. diff --git a/chromium/content/browser/renderer_host/web_input_event_aurax11.cc b/chromium/content/browser/renderer_host/web_input_event_aurax11.cc index fdde17edc7b..76ae55f5f19 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aurax11.cc +++ b/chromium/content/browser/renderer_host/web_input_event_aurax11.cc @@ -45,10 +45,10 @@ #include "base/event_types.h" #include "base/logging.h" #include "content/browser/renderer_host/ui_events_helper.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_constants.h" -#include "ui/base/keycodes/keyboard_code_conversion_x.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace content { @@ -87,106 +87,8 @@ int XKeyEventToWindowsKeyCode(XKeyEvent* event) { return windows_key_code; } -// From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp: -WebKit::WebUChar GetControlCharacter(int windows_key_code, bool shift) { - if (windows_key_code >= ui::VKEY_A && - windows_key_code <= ui::VKEY_Z) { - // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A - return windows_key_code - ui::VKEY_A + 1; - } - if (shift) { - // following graphics chars require shift key to input. - switch (windows_key_code) { - // ctrl-@ maps to \x00 (Null byte) - case ui::VKEY_2: - return 0; - // ctrl-^ maps to \x1E (Record separator, Information separator two) - case ui::VKEY_6: - return 0x1E; - // ctrl-_ maps to \x1F (Unit separator, Information separator one) - case ui::VKEY_OEM_MINUS: - return 0x1F; - // Returns 0 for all other keys to avoid inputting unexpected chars. - default: - break; - } - } else { - switch (windows_key_code) { - // ctrl-[ maps to \x1B (Escape) - case ui::VKEY_OEM_4: - return 0x1B; - // ctrl-\ maps to \x1C (File separator, Information separator four) - case ui::VKEY_OEM_5: - return 0x1C; - // ctrl-] maps to \x1D (Group separator, Information separator three) - case ui::VKEY_OEM_6: - return 0x1D; - // ctrl-Enter maps to \x0A (Line feed) - case ui::VKEY_RETURN: - return 0x0A; - // Returns 0 for all other keys to avoid inputting unexpected chars. - default: - break; - } - } - return 0; -} - } // namespace -WebKit::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent( - ui::ScrollEvent* event) { - WebKit::WebMouseWheelEvent webkit_event; - - webkit_event.type = WebKit::WebInputEvent::MouseWheel; - webkit_event.button = WebKit::WebMouseEvent::ButtonNone; - webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); - webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); - webkit_event.hasPreciseScrollingDeltas = true; - webkit_event.deltaX = event->x_offset(); - if (event->x_offset_ordinal() != 0.f && event->x_offset() != 0.f) { - webkit_event.accelerationRatioX = - event->x_offset_ordinal() / event->x_offset(); - } - webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick; - webkit_event.deltaY = event->y_offset(); - webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick; - if (event->y_offset_ordinal() != 0.f && event->y_offset() != 0.f) { - webkit_event.accelerationRatioY = - event->y_offset_ordinal() / event->y_offset(); - } - - return webkit_event; -} - -// NOTE: ui::ScrollEvent instances come from the touchpad. -WebKit::WebGestureEvent MakeWebGestureEventFromAuraEvent( - ui::ScrollEvent* event) { - WebKit::WebGestureEvent webkit_event; - - switch (event->type()) { - case ui::ET_SCROLL_FLING_START: - webkit_event.type = WebKit::WebInputEvent::GestureFlingStart; - webkit_event.data.flingStart.velocityX = event->x_offset(); - webkit_event.data.flingStart.velocityY = event->y_offset(); - break; - case ui::ET_SCROLL_FLING_CANCEL: - webkit_event.type = WebKit::WebInputEvent::GestureFlingCancel; - break; - case ui::ET_SCROLL: - NOTREACHED() << "Invalid gesture type: " << event->type(); - break; - default: - NOTREACHED() << "Unknown gesture type: " << event->type(); - } - - webkit_event.sourceDevice = WebKit::WebGestureEvent::Touchpad; - webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); - webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); - - return webkit_event; -} - WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( ui::KeyEvent* event) { base::NativeEvent native_event = event->native_event(); |