Index: content/renderer/render_widget.cc |
=================================================================== |
--- content/renderer/render_widget.cc (revision 221161) |
+++ content/renderer/render_widget.cc (working copy) |
@@ -53,10 +53,11 @@ |
#include "third_party/WebKit/public/web/WebScreenInfo.h" |
#include "third_party/skia/include/core/SkShader.h" |
#include "ui/base/ui_base_switches.h" |
-#include "ui/gfx/point.h" |
+#include "ui/gfx/point_conversions.h" |
#include "ui/gfx/rect_conversions.h" |
#include "ui/gfx/size_conversions.h" |
#include "ui/gfx/skia_util.h" |
+#include "ui/gfx/vector2d_conversions.h" |
#include "ui/gl/gl_switches.h" |
#include "ui/surface/transport_dib.h" |
#include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h" |
@@ -79,6 +80,7 @@ |
using WebKit::WebInputEvent; |
using WebKit::WebKeyboardEvent; |
using WebKit::WebMouseEvent; |
+using WebKit::WebMouseWheelEvent; |
using WebKit::WebNavigationPolicy; |
using WebKit::WebPagePopup; |
using WebKit::WebPoint; |
@@ -386,6 +388,7 @@ |
IPC_MESSAGE_HANDLER(ViewMsg_UpdateRect_ACK, OnUpdateRectAck) |
IPC_MESSAGE_HANDLER(ViewMsg_SwapBuffers_ACK, |
OnViewContextSwapBuffersComplete) |
+ IPC_MESSAGE_HANDLER(ViewMsg_EmulateDevice_ACK, OnEmulateDeviceAck) |
IPC_MESSAGE_HANDLER(ViewMsg_SetInputMethodActive, OnSetInputMethodActive) |
IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition) |
IPC_MESSAGE_HANDLER(ViewMsg_ImeConfirmComposition, OnImeConfirmComposition) |
@@ -522,6 +525,53 @@ |
Release(); |
} |
+void RenderWidget::EmulateDevice( |
+ bool enabled, |
+ const gfx::Size& device_size, |
+ const gfx::Rect& widget_rect, |
+ float device_scale_factor, |
+ bool fit_to_view) { |
+ pending_device_emulation_enabled_ = enabled; |
+ if (enabled) { |
+ pending_device_emulation_helper_.reset(new DeviceEmulationHelper( |
+ device_size, |
+ widget_rect, |
+ device_scale_factor, |
+ fit_to_view)); |
+ } else { |
+ pending_device_emulation_helper_.reset(); |
+ } |
+ Send(new ViewHostMsg_EmulateDevice(routing_id_, enabled)); |
+} |
+ |
+void RenderWidget::OnEmulateDeviceAck(bool enabled) { |
+ // We may have got another emulate device request while sending message |
+ // to the host. If the host idea of whether device emulation is enabled |
+ // differs, wait for another ack with the right |enabled| value. |
+ if (enabled != pending_device_emulation_enabled_) |
+ return; |
+ |
+ if (pending_device_emulation_enabled_) { |
+ if (!device_emulation_helper_) { |
+ device_emulation_helper_ = pending_device_emulation_helper_.Pass(); |
+ device_emulation_helper_->BeginEmulation(this); |
+ } else { |
+ device_emulation_helper_->ChangeEmulationParams( |
+ this, pending_device_emulation_helper_.get()); |
+ pending_device_emulation_helper_.reset(); |
+ } |
+ } else { |
+ if (device_emulation_helper_) |
+ device_emulation_helper_->EndEmulation(this); |
+ device_emulation_helper_.reset(); |
+ } |
+} |
+ |
+void RenderWidget::SetDeviceEmulationScale(float scale) { |
+ // This is only supported in RenderView. |
+ NOTREACHED(); |
+} |
+ |
// Got a response from the browser after the renderer decided to create a new |
// view. |
void RenderWidget::OnCreatingNewAck() { |
@@ -531,6 +581,11 @@ |
} |
void RenderWidget::OnResize(const ViewMsg_Resize_Params& params) { |
+ if (device_emulation_helper_) { |
+ device_emulation_helper_->OnResizeMessage(this, params); |
+ return; |
+ } |
+ |
screen_info_ = params.screen_info; |
SetDeviceScaleFactor(screen_info_.deviceScaleFactor); |
Resize(params.new_size, params.physical_backing_size, |
@@ -813,6 +868,21 @@ |
const ui::LatencyInfo& latency_info, |
bool is_keyboard_shortcut) { |
handling_input_event_ = true; |
+ |
+ scoped_ptr<const WebKit::WebInputEvent> owner; |
+ if (device_emulation_helper_) { |
+ input_event = device_emulation_helper_->ConvertInputEventToEmulated( |
+ this, input_event); |
+ if (input_event) |
+ owner.reset(input_event); |
+ } |
+ if (popup_device_emulation_helper_) { |
+// input_event = popup_device_emulation_helper_->ConvertInputEventToEmulated( |
+// this, input_event); |
+// if (input_event) |
+// owner.reset(input_event); |
+ } |
+ |
if (!input_event) { |
handling_input_event_ = false; |
return; |
@@ -1425,6 +1495,8 @@ |
// The invalidated rect might be outside the bounds of the view. |
gfx::Rect view_rect(size_); |
gfx::Rect damaged_rect = gfx::IntersectRects(view_rect, rect); |
+ if (popup_device_emulation_helper_) |
+ damaged_rect = view_rect; |
if (damaged_rect.IsEmpty()) |
return; |
@@ -1776,7 +1848,13 @@ |
Send(new ViewHostMsg_SetTooltipText(routing_id_, text, hint)); |
} |
-void RenderWidget::setWindowRect(const WebRect& pos) { |
+void RenderWidget::setWindowRect(const WebRect& rect) { |
+ WebRect pos = rect; |
+ if (popup_device_emulation_helper_) { |
+ pos = popup_device_emulation_helper_->ConvertPopupScreenRectFromEmulated( |
+ this, rect); |
+ } |
+ |
if (did_show_) { |
if (!RenderThreadImpl::current()->layout_test_mode()) { |
Send(new ViewHostMsg_RequestMove(routing_id_, pos)); |
@@ -2017,6 +2095,11 @@ |
void RenderWidget::OnUpdateScreenRects(const gfx::Rect& view_screen_rect, |
const gfx::Rect& window_screen_rect) { |
+ if (device_emulation_helper_) { |
+ device_emulation_helper_->OnUpdateScreenRectsMessage( |
+ this, view_screen_rect, window_screen_rect); |
+ return; |
+ } |
view_screen_rect_ = view_screen_rect; |
window_screen_rect_ = window_screen_rect; |
Send(new ViewHostMsg_UpdateScreenRects_ACK(routing_id())); |
@@ -2531,4 +2614,269 @@ |
return context.Pass(); |
} |
+// This class scales input event's coordinates, and then sets screen coordinates |
+// to emulate specific view position on the screen. |
+class DeviceEmulationEventRewriter : public InputEventRewriter { |
+ public: |
+ DeviceEmulationEventRewriter() {} |
+ virtual ~DeviceEmulationEventRewriter() {} |
+ |
+ void SetParams(float scale, const gfx::Point& origin); |
+ |
+ // InputEventRewriter implementation. |
+ virtual WebKit::WebInputEvent* RewriteInputEvent( |
+ const WebKit::WebInputEvent* event); |
+ |
+ private: |
+ void ConvertMouseEventToEmulated(WebKit::WebMouseEvent* event); |
+ void ConvertTouchPointToEmulated(WebKit::WebTouchPoint* point); |
+ |
+ // Event coordinates are scaled by this value. |
+ float scale_; |
+ |
+ // Widget screen position, used to set proper event screen coordinates. |
+ gfx::Point origin_; |
+}; |
+ |
+void DeviceEmulationEventRewriter::SetParams( |
+ float scale, const gfx::Point& origin) { |
+ scale_ = scale; |
+ origin_ = origin; |
+} |
+ |
+WebKit::WebInputEvent* DeviceEmulationEventRewriter::RewriteInputEvent( |
+ const WebKit::WebInputEvent* event) { |
+ if (!event) |
+ return NULL; |
+ |
+ if (WebInputEvent::isKeyboardEventType(event->type)) { |
+ const WebKeyboardEvent& keyboard_event = |
+ *static_cast<const WebKeyboardEvent*>(event); |
+ WebKeyboardEvent* result = new WebKeyboardEvent(keyboard_event); |
+ return result; |
+ } |
+ |
+ if (WebInputEvent::isMouseEventType(event->type)) { |
+ const WebMouseEvent& mouse_event = |
+ *static_cast<const WebMouseEvent*>(event); |
+ WebMouseEvent* result = new WebMouseEvent(mouse_event); |
+ ConvertMouseEventToEmulated(result); |
+ return result; |
+ } |
+ |
+ if (WebInputEvent::MouseWheel == event->type) { |
+ const WebMouseWheelEvent& mouse_wheel_event = |
+ *static_cast<const WebMouseWheelEvent*>(event); |
+ WebMouseWheelEvent* result = new WebMouseWheelEvent(mouse_wheel_event); |
+ ConvertMouseEventToEmulated(result); |
+ return result; |
+ } |
+ |
+ if (WebInputEvent::isGestureEventType(event->type)) { |
+ const WebGestureEvent& gesture_event = |
+ *static_cast<const WebGestureEvent*>(event); |
+ WebGestureEvent* result = new WebGestureEvent(gesture_event); |
+ result->x = 1.f / scale_ * result->x; |
+ result->y = 1.f / scale_ * result->y; |
+ result->globalX = origin_.x() + result->x; |
+ result->globalY = origin_.y() + result->y; |
+ return result; |
+ } |
+ |
+ if (WebInputEvent::isTouchEventType(event->type)) { |
+ const WebTouchEvent& touch_event = |
+ *static_cast<const WebTouchEvent*>(event); |
+ WebTouchEvent* result = new WebTouchEvent(touch_event); |
+ for (size_t index = 0; index < result->touchesLength; ++index) { |
+ ConvertTouchPointToEmulated(&result->touches[index]); |
+ } |
+ for (size_t index = 0; index < result->changedTouchesLength; ++index) { |
+ ConvertTouchPointToEmulated(&result->changedTouches[index]); |
+ } |
+ for (size_t index = 0; index < result->targetTouchesLength; ++index) { |
+ ConvertTouchPointToEmulated(&result->targetTouches[index]); |
+ } |
+ return result; |
+ } |
+ |
+ return NULL; |
+} |
+ |
+void DeviceEmulationEventRewriter::ConvertMouseEventToEmulated( |
+ WebKit::WebMouseEvent* event) { |
+ event->x = 1.f / scale_ * event->x; |
+ event->y = 1.f / scale_ * event->y; |
+ event->movementX = 1.f / scale_ * event->movementX; |
+ event->movementY = 1.f / scale_ * event->movementY; |
+ event->windowX = origin_.x() + event->x; |
+ event->windowY = origin_.y() + event->y; |
+ event->globalX = event->windowX; |
+ event->globalY = event->windowY; |
+} |
+ |
+void DeviceEmulationEventRewriter::ConvertTouchPointToEmulated( |
+ WebKit::WebTouchPoint* point) { |
+ point->position.x = 1.f / scale_ * point->position.x; |
+ point->position.y = 1.f / scale_ * point->position.y; |
+ point->screenPosition.x = origin_.x() + point->position.x; |
+ point->screenPosition.y = origin_.y() + point->position.y; |
+} |
+ |
+ |
+RenderWidget::DeviceEmulationHelper::DeviceEmulationHelper( |
+ const gfx::Size& device_size, |
+ const gfx::Rect& widget_rect, |
+ float device_scale_factor, |
+ bool fit_to_view) |
+ : device_size_(device_size), |
+ widget_rect_(widget_rect), |
+ device_scale_factor_(device_scale_factor), |
+ fit_to_view_(fit_to_view), |
+ weak_ptr_factory_(this) { |
+} |
+ |
+RenderWidget::DeviceEmulationHelper::~DeviceEmulationHelper() { |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::BeginEmulation(RenderWidget* widget) { |
+ original_size_ = widget->size_; |
+ original_physical_backing_size_ = widget->physical_backing_size_; |
+ original_screen_info_ = widget->screen_info_; |
+ original_view_screen_rect_ = widget->view_screen_rect_; |
+ original_window_screen_rect_ = widget->window_screen_rect_; |
+ input_event_rewriter_.reset(new DeviceEmulationEventRewriter); |
+ Apply(widget, widget->overdraw_bottom_height_, widget->resizer_rect_, |
+ widget->is_fullscreen_); |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::ChangeEmulationParams( |
+ RenderWidget* widget, DeviceEmulationHelper* params) { |
+ device_size_ = params->device_size_; |
+ widget_rect_ = params->widget_rect_; |
+ device_scale_factor_ = params->device_scale_factor_; |
+ fit_to_view_ = params->fit_to_view_; |
+ Apply(widget, widget->overdraw_bottom_height_, widget->resizer_rect_, |
+ widget->is_fullscreen_); |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::EndEmulation(RenderWidget* widget) { |
+ widget->screen_info_ = original_screen_info_; |
+ if (widget->compositor_) { |
+ // Passing zero cancels the override. |
+ widget->compositor_->OverrideDeviceScaleFactor(0.f); |
+ } |
+ widget->SetDeviceScaleFactor(original_screen_info_.deviceScaleFactor); |
+ widget->SetDeviceEmulationScale(1.f); |
+ widget->view_screen_rect_ = original_view_screen_rect_; |
+ widget->window_screen_rect_ = original_window_screen_rect_; |
+ widget->Resize(original_size_, |
+ original_physical_backing_size_, |
+ widget->overdraw_bottom_height_, widget->resizer_rect_, |
+ widget->is_fullscreen_, NO_RESIZE_ACK); |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::Apply(RenderWidget* widget, |
+ float overdraw_bottom_height, gfx::Rect resizer_rect, bool is_fullscreen) { |
+ if (fit_to_view_) { |
+ float width_ratio = !original_size_.width() ? 1.f : |
+ static_cast<float>(widget_rect_.width()) / original_size_.width(); |
+ float height_ratio = !original_size_.height() ? 1.f : |
+ static_cast<float>(widget_rect_.height()) / original_size_.height(); |
+ float ratio = std::max(1.0f, std::max(width_ratio, height_ratio)); |
+ scale_ = 1.f / ratio; |
+ } else { |
+ scale_ = 1.f; |
+ } |
+ |
+ input_event_rewriter_->SetParams(scale_, widget_rect_.origin()); |
+ InputHandlerManager* input_handler_manager = |
+ RenderThreadImpl::current()->input_handler_manager(); |
+ if (input_handler_manager) { |
+ // Make a copy to be owned by InputHandlerManager. |
+ scoped_ptr<DeviceEmulationEventRewriter> copy( |
+ new DeviceEmulationEventRewriter(*input_event_rewriter_)); |
+ input_handler_manager->SetInputEventRewriter( |
+ widget->routing_id_, copy.PassAs<InputEventRewriter>()); |
+ } |
+ |
+ widget->screen_info_.rect = gfx::Rect(device_size_); |
+ widget->screen_info_.availableRect = gfx::Rect(device_size_); |
+ widget->screen_info_.deviceScaleFactor = device_scale_factor_; |
+ |
+ if (widget->compositor_) { |
+ // We keep the real device scale factor in compositor to prevent unnecessary |
+ // scaling on browser side. This override ensures that compositor |
+ // will not change scale factor due to WebLayerTreeView's call. |
+ widget->compositor_->OverrideDeviceScaleFactor( |
+ original_screen_info_.deviceScaleFactor); |
+ } |
+ |
+ // In order to fit into view, WebView applies scaling transform to the |
+ // root graphics layer. |
+ widget->SetDeviceEmulationScale(scale_); |
+ |
+ widget->SetDeviceScaleFactor(device_scale_factor_); |
+ widget->view_screen_rect_ = widget_rect_; |
+ widget->window_screen_rect_ = widget->screen_info_.availableRect; |
+ |
+ gfx::Size physical_backing_size = gfx::ToCeiledSize(gfx::ScaleSize( |
+ widget_rect_.size(), original_screen_info_.deviceScaleFactor * scale_)); |
+ widget->Resize(widget_rect_.size(), physical_backing_size, |
+ overdraw_bottom_height, resizer_rect, |
+ is_fullscreen, NO_RESIZE_ACK); |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::OnResizeMessage( |
+ RenderWidget* widget, const ViewMsg_Resize_Params& params) { |
+ bool need_ack = params.new_size != original_size_; |
+ original_size_ = params.new_size; |
+ original_physical_backing_size_ = params.physical_backing_size; |
+ original_screen_info_ = params.screen_info; |
+ Apply(widget, params.overdraw_bottom_height, params.resizer_rect, |
+ params.is_fullscreen); |
+ |
+ if (need_ack) { |
+ widget->set_next_paint_is_resize_ack(); |
+ if (widget->is_accelerated_compositing_active_ && widget->compositor_) |
+ widget->compositor_->SetNeedsRedrawRect(gfx::Rect(widget->size_)); |
+ else |
+ widget->didInvalidateRect(gfx::Rect(widget->size_)); |
+ } |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::OnUpdateScreenRectsMessage( |
+ RenderWidget* widget, |
+ const gfx::Rect view_screen_rect, |
+ const gfx::Rect window_screen_rect) { |
+ original_view_screen_rect_ = view_screen_rect; |
+ original_window_screen_rect_ = window_screen_rect; |
+ widget->Send(new ViewHostMsg_UpdateScreenRects_ACK(widget->routing_id())); |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::OnShowContextMenu( |
+ RenderWidget* widget, ContextMenuParams* params) { |
+ params->x *= scale_; |
+ params->y *= scale_; |
+} |
+ |
+WebKit::WebInputEvent* RenderWidget::DeviceEmulationHelper::ConvertInputEventToEmulated( |
+ RenderWidget* widget, const WebKit::WebInputEvent* event) { |
+ return input_event_rewriter_->RewriteInputEvent(event); |
+} |
+ |
+void RenderWidget::DeviceEmulationHelper::PopupCreated( |
+ RenderWidget* widget, RenderWidget* popup) { |
+ popup->popup_device_emulation_helper_ = weak_ptr_factory_.GetWeakPtr(); |
+} |
+ |
+WebKit::WebRect RenderWidget::DeviceEmulationHelper::ConvertPopupScreenRectFromEmulated( |
+ RenderWidget* popup, const WebKit::WebRect& rect) { |
+ WebKit::WebRect result = rect; |
+ result.x = original_view_screen_rect_.x() + |
+ (result.x - widget_rect_.x()) * scale_; |
+ result.y = original_view_screen_rect_.y() + |
+ (result.y - widget_rect_.y()) * scale_; |
+ return result; |
+} |
+ |
} // namespace content |