1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/render_widget_host_view_win.h"
7 #include <InputScope.h>
9 #pragma comment(lib, "wtsapi32.lib")
15 #include "base/basictypes.h"
16 #include "base/bind.h"
17 #include "base/callback_helpers.h"
18 #include "base/command_line.h"
19 #include "base/debug/trace_event.h"
20 #include "base/i18n/rtl.h"
21 #include "base/metrics/histogram.h"
22 #include "base/threading/thread.h"
23 #include "base/win/metro.h"
24 #include "base/win/scoped_comptr.h"
25 #include "base/win/scoped_gdi_object.h"
26 #include "base/win/win_util.h"
27 #include "base/win/windows_version.h"
28 #include "base/win/wrapped_window_proc.h"
29 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
30 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
31 #include "content/browser/accessibility/browser_accessibility_win.h"
32 #include "content/browser/gpu/gpu_data_manager_impl.h"
33 #include "content/browser/gpu/gpu_process_host.h"
34 #include "content/browser/gpu/gpu_process_host_ui_shim.h"
35 #include "content/browser/renderer_host/backing_store.h"
36 #include "content/browser/renderer_host/backing_store_win.h"
37 #include "content/browser/renderer_host/input/web_input_event_builders_win.h"
38 #include "content/browser/renderer_host/render_process_host_impl.h"
39 #include "content/browser/renderer_host/render_widget_host_impl.h"
40 #include "content/browser/renderer_host/ui_events_helper.h"
41 #include "content/common/accessibility_messages.h"
42 #include "content/common/gpu/gpu_messages.h"
43 #include "content/common/plugin_constants_win.h"
44 #include "content/common/view_messages.h"
45 #include "content/common/webplugin_geometry.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/child_process_data.h"
48 #include "content/public/browser/content_browser_client.h"
49 #include "content/public/browser/native_web_keyboard_event.h"
50 #include "content/public/browser/notification_service.h"
51 #include "content/public/browser/notification_types.h"
52 #include "content/public/browser/render_view_host.h"
53 #include "content/public/common/content_switches.h"
54 #include "content/public/common/page_zoom.h"
55 #include "content/public/common/process_type.h"
56 #include "skia/ext/skia_utils_win.h"
57 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
58 #include "third_party/WebKit/public/web/WebInputEvent.h"
59 #include "third_party/skia/include/core/SkRegion.h"
60 #include "ui/base/ime/composition_text.h"
61 #include "ui/base/ime/win/imm32_manager.h"
62 #include "ui/base/ime/win/tsf_input_scope.h"
63 #include "ui/base/l10n/l10n_util_win.h"
64 #include "ui/base/touch/touch_device.h"
65 #include "ui/base/touch/touch_enabled.h"
66 #include "ui/base/ui_base_switches.h"
67 #include "ui/base/view_prop.h"
68 #include "ui/base/win/mouse_wheel_util.h"
69 #include "ui/base/win/touch_input.h"
70 #include "ui/events/event.h"
71 #include "ui/events/event_utils.h"
72 #include "ui/gfx/canvas.h"
73 #include "ui/gfx/rect.h"
74 #include "ui/gfx/rect_conversions.h"
75 #include "ui/gfx/screen.h"
76 #include "ui/gfx/sequential_id_generator.h"
77 #include "ui/gfx/text_elider.h"
78 #include "ui/gfx/win/dpi.h"
79 #include "ui/gfx/win/hwnd_util.h"
80 #include "webkit/common/cursors/webcursor.h"
81 #include "win8/util/win8_util.h"
83 using base::TimeDelta;
84 using base::TimeTicks;
86 using WebKit::WebInputEvent;
87 using WebKit::WebMouseEvent;
88 using WebKit::WebTextDirection;
93 // Tooltips will wrap after this width. Yes, wrap. Imagine that!
94 const int kTooltipMaxWidthPixels = 300;
96 // Maximum number of characters we allow in a tooltip.
97 const int kMaxTooltipLength = 1024;
99 // A custom MSAA object id used to determine if a screen reader is actively
100 // listening for MSAA events.
101 const int kIdCustom = 1;
103 // The delay before the compositor host window is destroyed. This gives the GPU
104 // process a grace period to stop referencing it.
105 const int kDestroyCompositorHostWindowDelay = 10000;
107 // In mouse lock mode, we need to prevent the (invisible) cursor from hitting
108 // the border of the view, in order to get valid movement information. However,
109 // forcing the cursor back to the center of the view after each mouse move
110 // doesn't work well. It reduces the frequency of useful WM_MOUSEMOVE messages
111 // significantly. Therefore, we move the cursor to the center of the view only
112 // if it approaches the border. |kMouseLockBorderPercentage| specifies the width
113 // of the border area, in percentage of the corresponding dimension.
114 const int kMouseLockBorderPercentage = 15;
116 // A callback function for EnumThreadWindows to enumerate and dismiss
117 // any owned popup windows.
118 BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) {
119 const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg);
121 if (::IsWindowVisible(window)) {
122 const HWND owner = ::GetWindow(window, GW_OWNER);
123 if (toplevel_hwnd == owner) {
124 ::PostMessage(window, WM_CANCELMODE, 0, 0);
131 void SendToGpuProcessHost(int gpu_host_id, scoped_ptr<IPC::Message> message) {
132 GpuProcessHost* gpu_process_host = GpuProcessHost::FromID(gpu_host_id);
133 if (!gpu_process_host)
136 gpu_process_host->Send(message.release());
139 void PostTaskOnIOThread(const tracked_objects::Location& from_here,
140 base::Closure task) {
141 BrowserThread::PostTask(BrowserThread::IO, from_here, task);
144 bool DecodeZoomGesture(HWND hwnd, const GESTUREINFO& gi,
145 PageZoom* zoom, POINT* zoom_center) {
146 static long start = 0;
147 static POINT zoom_first;
149 if (gi.dwFlags == GF_BEGIN) {
150 start = gi.ullArguments;
151 zoom_first.x = gi.ptsLocation.x;
152 zoom_first.y = gi.ptsLocation.y;
153 ScreenToClient(hwnd, &zoom_first);
157 if (gi.dwFlags == GF_END)
160 POINT zoom_second = {0};
161 zoom_second.x = gi.ptsLocation.x;
162 zoom_second.y = gi.ptsLocation.y;
163 ScreenToClient(hwnd, &zoom_second);
165 if (zoom_first.x == zoom_second.x && zoom_first.y == zoom_second.y)
168 zoom_center->x = (zoom_first.x + zoom_second.x) / 2;
169 zoom_center->y = (zoom_first.y + zoom_second.y) / 2;
172 static_cast<double>(gi.ullArguments)/static_cast<double>(start);
174 *zoom = zoom_factor >= 1 ? PAGE_ZOOM_IN : PAGE_ZOOM_OUT;
176 start = gi.ullArguments;
177 zoom_first = zoom_second;
181 bool DecodeScrollGesture(const GESTUREINFO& gi,
184 // Windows gestures are streams of messages with begin/end messages that
185 // separate each new gesture. We key off the begin message to reset
186 // the static variables.
187 static POINT last_pt;
188 static POINT start_pt;
190 if (gi.dwFlags == GF_BEGIN) {
193 start_pt.x = gi.ptsLocation.x;
194 start_pt.y = gi.ptsLocation.y;
196 delta->x = gi.ptsLocation.x - last_pt.x;
197 delta->y = gi.ptsLocation.y - last_pt.y;
199 last_pt.x = gi.ptsLocation.x;
200 last_pt.y = gi.ptsLocation.y;
205 WebKit::WebMouseWheelEvent MakeFakeScrollWheelEvent(HWND hwnd,
208 WebKit::WebMouseWheelEvent result;
209 result.type = WebInputEvent::MouseWheel;
210 result.timeStampSeconds = ::GetMessageTime() / 1000.0;
211 result.button = WebMouseEvent::ButtonNone;
212 result.globalX = start.x;
213 result.globalY = start.y;
214 // Map to window coordinates.
215 POINT client_point = { result.globalX, result.globalY };
216 MapWindowPoints(0, hwnd, &client_point, 1);
217 result.x = client_point.x;
218 result.y = client_point.y;
219 result.windowX = result.x;
220 result.windowY = result.y;
221 // Note that we support diagonal scrolling.
222 result.deltaX = static_cast<float>(delta.x);
223 result.wheelTicksX = WHEEL_DELTA;
224 result.deltaY = static_cast<float>(delta.y);
225 result.wheelTicksY = WHEEL_DELTA;
229 static const int kTouchMask = 0x7;
231 inline int GetTouchType(const TOUCHINPUT& point) {
232 return point.dwFlags & kTouchMask;
235 inline void SetTouchType(TOUCHINPUT* point, int type) {
236 point->dwFlags = (point->dwFlags & kTouchMask) | type;
239 ui::EventType ConvertToUIEvent(WebKit::WebTouchPoint::State t) {
241 case WebKit::WebTouchPoint::StatePressed:
242 return ui::ET_TOUCH_PRESSED;
243 case WebKit::WebTouchPoint::StateMoved:
244 return ui::ET_TOUCH_MOVED;
245 case WebKit::WebTouchPoint::StateStationary:
246 return ui::ET_TOUCH_STATIONARY;
247 case WebKit::WebTouchPoint::StateReleased:
248 return ui::ET_TOUCH_RELEASED;
249 case WebKit::WebTouchPoint::StateCancelled:
250 return ui::ET_TOUCH_CANCELLED;
252 DCHECK(false) << "Unexpected ui type. " << t;
253 return ui::ET_UNKNOWN;
257 // Creates a WebGestureEvent corresponding to the given |gesture|
258 WebKit::WebGestureEvent CreateWebGestureEvent(HWND hwnd,
259 const ui::GestureEvent& gesture) {
260 WebKit::WebGestureEvent gesture_event =
261 MakeWebGestureEventFromUIEvent(gesture);
263 POINT client_point = gesture.location().ToPOINT();
264 POINT screen_point = gesture.location().ToPOINT();
265 MapWindowPoints(hwnd, HWND_DESKTOP, &screen_point, 1);
267 gesture_event.x = client_point.x;
268 gesture_event.y = client_point.y;
269 gesture_event.globalX = screen_point.x;
270 gesture_event.globalY = screen_point.y;
272 return gesture_event;
275 WebKit::WebGestureEvent CreateFlingCancelEvent(double time_stamp) {
276 WebKit::WebGestureEvent gesture_event;
277 gesture_event.timeStampSeconds = time_stamp;
278 gesture_event.type = WebKit::WebGestureEvent::GestureFlingCancel;
279 gesture_event.sourceDevice = WebKit::WebGestureEvent::Touchscreen;
280 return gesture_event;
283 class TouchEventFromWebTouchPoint : public ui::TouchEvent {
285 TouchEventFromWebTouchPoint(const WebKit::WebTouchPoint& touch_point,
286 base::TimeDelta& timestamp)
287 : ui::TouchEvent(ConvertToUIEvent(touch_point.state),
288 touch_point.position,
291 set_radius(touch_point.radiusX, touch_point.radiusY);
292 set_rotation_angle(touch_point.rotationAngle);
293 set_force(touch_point.force);
294 set_flags(ui::GetModifiersFromKeyState());
297 virtual ~TouchEventFromWebTouchPoint() {}
300 DISALLOW_COPY_AND_ASSIGN(TouchEventFromWebTouchPoint);
303 bool ShouldSendPinchGesture() {
304 static bool pinch_allowed =
305 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch);
306 return pinch_allowed;
309 void GetScreenInfoForWindow(gfx::NativeViewId id,
310 WebKit::WebScreenInfo* results) {
311 HWND window = gfx::NativeViewFromId(id);
313 HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
315 MONITORINFOEX monitor_info;
316 monitor_info.cbSize = sizeof(MONITORINFOEX);
317 if (!base::win::GetMonitorInfoWrapper(monitor, &monitor_info))
321 dev_mode.dmSize = sizeof(dev_mode);
322 dev_mode.dmDriverExtra = 0;
323 EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode);
325 WebKit::WebScreenInfo screen_info;
326 screen_info.depth = dev_mode.dmBitsPerPel;
327 screen_info.depthPerComponent = 8;
328 screen_info.deviceScaleFactor = gfx::win::GetDeviceScaleFactor();
329 screen_info.isMonochrome = dev_mode.dmColor == DMCOLOR_MONOCHROME;
330 screen_info.rect = gfx::Rect(monitor_info.rcMonitor);
331 screen_info.availableRect = gfx::Rect(monitor_info.rcWork);
333 *results = screen_info;
338 const wchar_t kRenderWidgetHostHWNDClass[] = L"Chrome_RenderWidgetHostHWND";
340 // Wrapper for maintaining touchstate associated with a WebTouchEvent.
341 class WebTouchState {
343 explicit WebTouchState(const RenderWidgetHostViewWin* window);
345 // Updates the current touchpoint state with the supplied touches.
346 // Touches will be consumed only if they are of the same type (e.g. down,
347 // up, move). Returns the number of consumed touches.
348 size_t UpdateTouchPoints(TOUCHINPUT* points, size_t count);
350 // Marks all active touchpoints as released.
351 bool ReleaseTouchPoints();
353 // The contained WebTouchEvent.
354 const WebKit::WebTouchEvent& touch_event() { return touch_event_; }
356 // Returns if any touches are modified in the event.
357 bool is_changed() { return touch_event_.changedTouchesLength != 0; }
360 // Adds a touch point or returns NULL if there's not enough space.
361 WebKit::WebTouchPoint* AddTouchPoint(TOUCHINPUT* touch_input);
363 // Copy details from a TOUCHINPUT to an existing WebTouchPoint, returning
364 // true if the resulting point is a stationary move.
365 bool UpdateTouchPoint(WebKit::WebTouchPoint* touch_point,
366 TOUCHINPUT* touch_input);
368 // Find (or create) a mapping for _os_touch_id_.
369 unsigned int GetMappedTouch(unsigned int os_touch_id);
371 // Remove any mappings that are no longer in use.
372 void RemoveExpiredMappings();
374 WebKit::WebTouchEvent touch_event_;
375 const RenderWidgetHostViewWin* const window_;
377 ui::SequentialIDGenerator id_generator_;
379 DISALLOW_COPY_AND_ASSIGN(WebTouchState);
382 typedef void (*MetroSetFrameWindow)(HWND window);
383 typedef void (*MetroCloseFrameWindow)(HWND window);
385 ///////////////////////////////////////////////////////////////////////////////
386 // RenderWidgetHostViewWin, public:
388 RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget)
389 : render_widget_host_(RenderWidgetHostImpl::From(widget)),
390 compositor_host_window_(NULL),
391 hide_compositor_window_at_next_paint_(false),
392 track_mouse_leave_(false),
393 imm32_manager_(new ui::IMM32Manager),
394 ime_notification_(false),
395 capture_enter_key_(false),
396 about_to_validate_and_paint_(false),
397 close_on_deactivate_(false),
398 being_destroyed_(false),
400 tooltip_showing_(false),
403 text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
404 text_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT),
405 can_compose_inline_(true),
406 is_fullscreen_(false),
407 ignore_mouse_movement_(true),
408 composition_range_(gfx::Range::InvalidRange()),
409 touch_state_(new WebTouchState(this)),
410 pointer_down_context_(false),
411 last_touch_location_(-1, -1),
412 touch_events_enabled_(ui::AreTouchEventsEnabled()),
413 gesture_recognizer_(ui::GestureRecognizer::Create()) {
414 render_widget_host_->SetView(this);
416 NOTIFICATION_RENDERER_PROCESS_TERMINATED,
417 NotificationService::AllBrowserContextsAndSources());
418 gesture_recognizer_->AddGestureEventHelper(this);
421 RenderWidgetHostViewWin::~RenderWidgetHostViewWin() {
422 gesture_recognizer_->RemoveGestureEventHelper(this);
427 void RenderWidgetHostViewWin::CreateWnd(HWND parent) {
428 // ATL function to create the window.
432 ///////////////////////////////////////////////////////////////////////////////
433 // RenderWidgetHostViewWin, RenderWidgetHostView implementation:
435 void RenderWidgetHostViewWin::InitAsChild(
436 gfx::NativeView parent_view) {
437 CreateWnd(parent_view);
440 void RenderWidgetHostViewWin::InitAsPopup(
441 RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
442 close_on_deactivate_ = true;
443 DoPopupOrFullscreenInit(parent_host_view->GetNativeView(), pos,
447 void RenderWidgetHostViewWin::InitAsFullscreen(
448 RenderWidgetHostView* reference_host_view) {
449 gfx::Rect pos = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
450 reference_host_view->GetNativeView()).bounds();
451 is_fullscreen_ = true;
452 DoPopupOrFullscreenInit(gfx::GetWindowToParentTo(true), pos, 0);
455 RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const {
456 return render_widget_host_;
459 void RenderWidgetHostViewWin::WasShown() {
460 // |render_widget_host_| may be NULL if the WebContentsImpl is in the process
462 if (!render_widget_host_)
465 if (!render_widget_host_->is_hidden())
468 if (web_contents_switch_paint_time_.is_null())
469 web_contents_switch_paint_time_ = TimeTicks::Now();
471 render_widget_host_->WasShown();
474 void RenderWidgetHostViewWin::WasHidden() {
475 // |render_widget_host_| may be NULL if the WebContentsImpl is in the process
477 if (!render_widget_host_)
480 if (render_widget_host_->is_hidden())
485 // Inform the renderer that we are being hidden so it can reduce its resource
487 render_widget_host_->WasHidden();
489 if (accelerated_surface_)
490 accelerated_surface_->WasHidden();
492 if (GetBrowserAccessibilityManager())
493 GetBrowserAccessibilityManager()->WasHidden();
495 web_contents_switch_paint_time_ = base::TimeTicks();
498 void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) {
499 SetBounds(gfx::Rect(GetPixelBounds().origin(), size));
502 void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) {
503 if (being_destroyed_)
506 // No SWP_NOREDRAW as autofill popups can move and the underneath window
507 // should redraw in that case.
508 UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS |
509 SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE;
511 // If the style is not popup, you have to convert the point to client
513 POINT point = { rect.x(), rect.y() };
514 if (GetStyle() & WS_CHILD)
515 ScreenToClient(&point);
517 SetWindowPos(NULL, point.x, point.y, rect.width(), rect.height(), swp_flags);
518 render_widget_host_->WasResized();
521 gfx::NativeView RenderWidgetHostViewWin::GetNativeView() const {
525 gfx::NativeViewId RenderWidgetHostViewWin::GetNativeViewId() const {
526 return reinterpret_cast<gfx::NativeViewId>(m_hWnd);
529 gfx::NativeViewAccessible
530 RenderWidgetHostViewWin::GetNativeViewAccessible() {
531 if (render_widget_host_ &&
532 !BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
533 // Attempt to detect screen readers by sending an event with our custom id.
534 NotifyWinEvent(EVENT_SYSTEM_ALERT, m_hWnd, kIdCustom, CHILDID_SELF);
537 CreateBrowserAccessibilityManagerIfNeeded();
539 return GetBrowserAccessibilityManager()->GetRoot()->
540 ToBrowserAccessibilityWin();
543 void RenderWidgetHostViewWin::CreateBrowserAccessibilityManagerIfNeeded() {
544 if (GetBrowserAccessibilityManager())
547 HRESULT hr = ::CreateStdAccessibleObject(
548 m_hWnd, OBJID_WINDOW, IID_IAccessible,
549 reinterpret_cast<void **>(&window_iaccessible_));
550 DCHECK(SUCCEEDED(hr));
552 SetBrowserAccessibilityManager(
553 new BrowserAccessibilityManagerWin(
555 window_iaccessible_.get(),
556 BrowserAccessibilityManagerWin::GetEmptyDocument(),
560 void RenderWidgetHostViewWin::MovePluginWindows(
561 const gfx::Vector2d& scroll_offset,
562 const std::vector<WebPluginGeometry>& plugin_window_moves) {
563 MovePluginWindowsHelper(m_hWnd, plugin_window_moves);
566 static BOOL CALLBACK AddChildWindowToVector(HWND hwnd, LPARAM lparam) {
567 std::vector<HWND>* vector = reinterpret_cast<std::vector<HWND>*>(lparam);
568 vector->push_back(hwnd);
572 void RenderWidgetHostViewWin::CleanupCompositorWindow() {
573 if (!compositor_host_window_)
576 gfx::SetWindowUserData(compositor_host_window_, NULL);
578 // Hide the compositor and parent it to the desktop rather than destroying
579 // it immediately. The GPU process has a grace period to stop accessing the
580 // window. TODO(apatrick): the GPU process should acknowledge that it has
581 // finished with the window handle and the browser process should destroy it
583 ::ShowWindow(compositor_host_window_, SW_HIDE);
584 ::SetParent(compositor_host_window_, NULL);
586 BrowserThread::PostDelayedTask(
589 base::Bind(base::IgnoreResult(&::DestroyWindow),
590 compositor_host_window_),
591 base::TimeDelta::FromMilliseconds(kDestroyCompositorHostWindowDelay));
593 compositor_host_window_ = NULL;
596 bool RenderWidgetHostViewWin::IsActivatable() const {
597 // Popups should not be activated.
598 return popup_type_ == WebKit::WebPopupTypeNone;
601 void RenderWidgetHostViewWin::Focus() {
606 void RenderWidgetHostViewWin::Blur() {
610 bool RenderWidgetHostViewWin::HasFocus() const {
611 return ::GetFocus() == m_hWnd;
614 bool RenderWidgetHostViewWin::IsSurfaceAvailableForCopy() const {
615 if (render_widget_host_->is_accelerated_compositing_active())
616 return accelerated_surface_.get() && accelerated_surface_->IsReadyForCopy();
618 return !!render_widget_host_->GetBackingStore(false);
621 void RenderWidgetHostViewWin::Show() {
626 void RenderWidgetHostViewWin::Hide() {
627 if (!is_fullscreen_ && GetParent() == gfx::GetWindowToParentTo(true)) {
628 LOG(WARNING) << "Hide() called twice in a row: " << this << ":"
633 if (::GetFocus() == m_hWnd)
640 bool RenderWidgetHostViewWin::IsShowing() {
641 return !!IsWindowVisible();
644 gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const {
645 return gfx::win::ScreenToDIPRect(GetPixelBounds());
648 gfx::Rect RenderWidgetHostViewWin::GetPixelBounds() const {
650 GetWindowRect(&window_rect);
651 return gfx::Rect(window_rect);
654 void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) {
655 current_cursor_ = cursor;
656 UpdateCursorIfOverSelf();
659 void RenderWidgetHostViewWin::UpdateCursorIfOverSelf() {
660 static HCURSOR kCursorArrow = LoadCursor(NULL, IDC_ARROW);
661 static HCURSOR kCursorAppStarting = LoadCursor(NULL, IDC_APPSTARTING);
662 static HINSTANCE module_handle = GetModuleHandle(
663 GetContentClient()->browser()->GetResourceDllName());
665 // If the mouse is over our HWND, then update the cursor state immediately.
668 if (WindowFromPoint(pt) == m_hWnd) {
669 // We cannot pass in NULL as the module handle as this would only work for
670 // standard win32 cursors. We can also receive cursor types which are
671 // defined as webkit resources. We need to specify the module handle of
672 // chrome.dll while loading these cursors.
673 HCURSOR display_cursor = current_cursor_.GetCursor(module_handle);
675 // If a page is in the loading state, we want to show the Arrow+Hourglass
676 // cursor only when the current cursor is the ARROW cursor. In all other
677 // cases we should continue to display the current cursor.
678 if (is_loading_ && display_cursor == kCursorArrow)
679 display_cursor = kCursorAppStarting;
681 SetCursor(display_cursor);
685 void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) {
686 is_loading_ = is_loading;
687 UpdateCursorIfOverSelf();
690 void RenderWidgetHostViewWin::TextInputTypeChanged(
691 ui::TextInputType type,
692 ui::TextInputMode input_mode,
693 bool can_compose_inline) {
694 if (text_input_type_ != type ||
695 text_input_mode_ != input_mode ||
696 can_compose_inline_ != can_compose_inline) {
697 const bool text_input_type_changed = (text_input_type_ != type) ||
698 (text_input_mode_ != input_mode);
699 text_input_type_ = type;
700 text_input_mode_ = input_mode;
701 can_compose_inline_ = can_compose_inline;
703 if (text_input_type_changed)
704 UpdateInputScopeIfNecessary(text_input_type_);
708 void RenderWidgetHostViewWin::SelectionBoundsChanged(
709 const ViewHostMsg_SelectionBounds_Params& params) {
710 bool is_enabled = (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
711 text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD);
712 // Only update caret position if the input method is enabled.
714 caret_rect_ = gfx::UnionRects(params.anchor_rect, params.focus_rect);
715 imm32_manager_->UpdateCaretRect(m_hWnd, caret_rect_);
719 void RenderWidgetHostViewWin::ScrollOffsetChanged() {
722 void RenderWidgetHostViewWin::ImeCancelComposition() {
723 imm32_manager_->CancelIME(m_hWnd);
726 void RenderWidgetHostViewWin::ImeCompositionRangeChanged(
727 const gfx::Range& range,
728 const std::vector<gfx::Rect>& character_bounds) {
729 composition_range_ = range;
730 composition_character_bounds_ = character_bounds;
733 void RenderWidgetHostViewWin::Redraw() {
735 GetUpdateRect(&damage_bounds, FALSE);
737 base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0));
738 GetUpdateRgn(damage_region, FALSE);
740 // Paint the invalid region synchronously. Our caller will not paint again
741 // until we return, so by painting to the screen here, we ensure effective
742 // rate-limiting of backing store updates. This helps a lot on pages that
743 // have animations or fairly expensive layout (e.g., google maps).
745 // We paint this window synchronously, however child windows (i.e. plugins)
746 // are painted asynchronously. By avoiding synchronous cross-process window
747 // message dispatching we allow scrolling to be smooth, and also avoid the
748 // browser process locking up if the plugin process is hung.
750 RedrawWindow(NULL, damage_region, RDW_UPDATENOW | RDW_NOCHILDREN);
752 // Send the invalid rect in screen coordinates.
753 gfx::Rect invalid_screen_rect(damage_bounds);
754 invalid_screen_rect.Offset(GetPixelBounds().OffsetFromOrigin());
756 PaintPluginWindowsHelper(m_hWnd, invalid_screen_rect);
759 void RenderWidgetHostViewWin::DidUpdateBackingStore(
760 const gfx::Rect& scroll_rect,
761 const gfx::Vector2d& scroll_delta,
762 const std::vector<gfx::Rect>& copy_rects,
763 const ui::LatencyInfo& latency_info) {
764 TRACE_EVENT0("content", "RenderWidgetHostViewWin::DidUpdateBackingStore");
765 software_latency_info_.MergeWith(latency_info);
766 if (render_widget_host_->is_hidden())
769 // Schedule invalidations first so that the ScrollWindowEx call is closer to
770 // Redraw. That minimizes chances of "flicker" resulting if the screen
771 // refreshes before we have a chance to paint the exposed area. Somewhat
772 // surprisingly, this ordering matters.
774 for (size_t i = 0; i < copy_rects.size(); ++i) {
775 gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(copy_rects[i]);
776 // Damage might not be DIP aligned.
777 pixel_rect.Inset(-1, -1);
778 RECT bounds = pixel_rect.ToRECT();
779 InvalidateRect(&bounds, false);
782 if (!scroll_rect.IsEmpty()) {
783 TRACE_EVENT0("content", "ScrollWindowEx");
784 gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(scroll_rect);
785 // Damage might not be DIP aligned.
786 pixel_rect.Inset(-1, -1);
787 RECT clip_rect = pixel_rect.ToRECT();
788 float scale = gfx::win::GetDeviceScaleFactor();
789 int dx = static_cast<int>(scale * scroll_delta.x());
790 int dy = static_cast<int>(scale * scroll_delta.y());
791 ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE);
794 if (!about_to_validate_and_paint_)
798 void RenderWidgetHostViewWin::RenderProcessGone(base::TerminationStatus status,
800 UpdateCursorIfOverSelf();
804 bool RenderWidgetHostViewWin::CanSubscribeFrame() const {
805 return render_widget_host_ != NULL;
808 void RenderWidgetHostViewWin::WillWmDestroy() {
809 CleanupCompositorWindow();
810 if (base::win::IsTSFAwareRequired())
811 ui::TSFBridge::GetInstance()->RemoveFocusedClient(this);
814 void RenderWidgetHostViewWin::Destroy() {
815 // We've been told to destroy.
816 // By clearing close_on_deactivate_, we prevent further deactivations
817 // (caused by windows messages resulting from the DestroyWindow) from
818 // triggering further destructions. The deletion of this is handled by
820 close_on_deactivate_ = false;
821 render_widget_host_ = NULL;
822 being_destroyed_ = true;
823 CleanupCompositorWindow();
825 // This releases the resources associated with input scope.
826 UpdateInputScopeIfNecessary(ui::TEXT_INPUT_TYPE_NONE);
828 if (is_fullscreen_ && win8::IsSingleWindowMetroMode()) {
829 MetroCloseFrameWindow close_frame_window =
830 reinterpret_cast<MetroCloseFrameWindow>(
831 ::GetProcAddress(base::win::GetMetroModule(), "CloseFrameWindow"));
832 DCHECK(close_frame_window);
833 close_frame_window(m_hWnd);
839 void RenderWidgetHostViewWin::SetTooltipText(const string16& tooltip_text) {
840 if (!render_widget_host_->is_hidden())
843 // Clamp the tooltip length to kMaxTooltipLength so that we don't
844 // accidentally DOS the user with a mega tooltip (since Windows doesn't seem
845 // to do this itself).
846 const string16 new_tooltip_text =
847 gfx::TruncateString(tooltip_text, kMaxTooltipLength);
849 if (new_tooltip_text != tooltip_text_) {
850 tooltip_text_ = new_tooltip_text;
852 // Need to check if the tooltip is already showing so that we don't
853 // immediately show the tooltip with no delay when we move the mouse from
854 // a region with no tooltip to a region with a tooltip.
855 if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) {
856 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
857 ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
860 // Make sure the tooltip gets closed after TTN_POP gets sent. For some
861 // reason this doesn't happen automatically, so moving the mouse around
862 // within the same link/image/etc doesn't cause the tooltip to re-appear.
863 if (!tooltip_showing_) {
864 if (::IsWindow(tooltip_hwnd_))
865 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
870 BackingStore* RenderWidgetHostViewWin::AllocBackingStore(
871 const gfx::Size& size) {
872 return new BackingStoreWin(render_widget_host_, size);
875 void RenderWidgetHostViewWin::CopyFromCompositingSurface(
876 const gfx::Rect& src_subrect,
877 const gfx::Size& dst_size,
878 const base::Callback<void(bool, const SkBitmap&)>& callback) {
879 base::ScopedClosureRunner scoped_callback_runner(
880 base::Bind(callback, false, SkBitmap()));
881 if (!accelerated_surface_)
884 if (dst_size.IsEmpty() || src_subrect.IsEmpty())
887 ignore_result(scoped_callback_runner.Release());
888 accelerated_surface_->AsyncCopyTo(src_subrect, dst_size, callback);
891 void RenderWidgetHostViewWin::CopyFromCompositingSurfaceToVideoFrame(
892 const gfx::Rect& src_subrect,
893 const scoped_refptr<media::VideoFrame>& target,
894 const base::Callback<void(bool)>& callback) {
895 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false));
896 if (!accelerated_surface_)
899 if (!target || (target->format() != media::VideoFrame::YV12 &&
900 target->format() != media::VideoFrame::I420))
903 if (src_subrect.IsEmpty())
906 ignore_result(scoped_callback_runner.Release());
907 accelerated_surface_->AsyncCopyToVideoFrame(src_subrect, target, callback);
910 bool RenderWidgetHostViewWin::CanCopyToVideoFrame() const {
911 return accelerated_surface_.get() && render_widget_host_ &&
912 render_widget_host_->is_accelerated_compositing_active();
915 void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) {
916 RenderWidgetHostViewBase::SetBackground(background);
917 render_widget_host_->SetBackground(background);
920 void RenderWidgetHostViewWin::ProcessAckedTouchEvent(
921 const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
922 DCHECK(touch_events_enabled_);
924 ScopedVector<ui::TouchEvent> events;
925 if (!MakeUITouchEventsFromWebTouchEvents(touch, &events, LOCAL_COORDINATES))
928 ui::EventResult result = (ack_result ==
929 INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
930 for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
931 end = events.end(); iter != end; ++iter) {
932 scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
933 gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
934 *(*iter), result, this));
935 ProcessGestures(gestures.get());
939 void RenderWidgetHostViewWin::UpdateDesiredTouchMode() {
940 // Make sure that touch events even make sense.
941 if (base::win::GetVersion() < base::win::VERSION_WIN7)
943 if (touch_events_enabled_) {
944 CHECK(RegisterTouchWindow(m_hWnd, TWF_WANTPALM));
948 bool RenderWidgetHostViewWin::CanDispatchToConsumer(
949 ui::GestureConsumer* consumer) {
950 CHECK_EQ(static_cast<RenderWidgetHostViewWin*>(consumer), this);
954 void RenderWidgetHostViewWin::DispatchPostponedGestureEvent(
955 ui::GestureEvent* event) {
956 ForwardGestureEventToRenderer(event);
959 void RenderWidgetHostViewWin::DispatchCancelTouchEvent(
960 ui::TouchEvent* event) {
961 if (!render_widget_host_ || !touch_events_enabled_ ||
962 !render_widget_host_->ShouldForwardTouchEvent()) {
965 DCHECK(event->type() == WebKit::WebInputEvent::TouchCancel);
966 WebKit::WebTouchEvent cancel_event;
967 cancel_event.type = WebKit::WebInputEvent::TouchCancel;
968 cancel_event.timeStampSeconds = event->time_stamp().InSecondsF();
969 render_widget_host_->ForwardTouchEventWithLatencyInfo(
970 cancel_event, *event->latency());
973 void RenderWidgetHostViewWin::SetHasHorizontalScrollbar(
974 bool has_horizontal_scrollbar) {
977 void RenderWidgetHostViewWin::SetScrollOffsetPinning(
978 bool is_pinned_to_left, bool is_pinned_to_right) {
981 void RenderWidgetHostViewWin::SetCompositionText(
982 const ui::CompositionText& composition) {
983 if (!base::win::IsTSFAwareRequired()) {
987 if (!render_widget_host_)
989 // ui::CompositionUnderline should be identical to
990 // WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely.
991 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
992 sizeof(WebKit::WebCompositionUnderline),
993 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
994 const std::vector<WebKit::WebCompositionUnderline>& underlines =
995 reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>(
996 composition.underlines);
997 render_widget_host_->ImeSetComposition(composition.text, underlines,
998 composition.selection.end(),
999 composition.selection.end());
1002 void RenderWidgetHostViewWin::ConfirmCompositionText() {
1003 if (!base::win::IsTSFAwareRequired()) {
1007 // TODO(nona): Implement this function.
1011 void RenderWidgetHostViewWin::ClearCompositionText() {
1012 if (!base::win::IsTSFAwareRequired()) {
1016 // TODO(nona): Implement this function.
1020 void RenderWidgetHostViewWin::InsertText(const string16& text) {
1021 if (!base::win::IsTSFAwareRequired()) {
1025 if (render_widget_host_)
1026 render_widget_host_->ImeConfirmComposition(text,
1027 gfx::Range::InvalidRange(),
1031 void RenderWidgetHostViewWin::InsertChar(char16 ch, int flags) {
1032 if (!base::win::IsTSFAwareRequired()) {
1036 // TODO(nona): Implement this function.
1040 gfx::NativeWindow RenderWidgetHostViewWin::GetAttachedWindow() const {
1044 ui::TextInputType RenderWidgetHostViewWin::GetTextInputType() const {
1045 if (!base::win::IsTSFAwareRequired()) {
1047 return ui::TEXT_INPUT_TYPE_NONE;
1049 return text_input_type_;
1052 ui::TextInputMode RenderWidgetHostViewWin::GetTextInputMode() const {
1053 if (!base::win::IsTSFAwareRequired()) {
1055 return ui::TEXT_INPUT_MODE_DEFAULT;
1057 return text_input_mode_;
1060 bool RenderWidgetHostViewWin::CanComposeInline() const {
1061 if (!base::win::IsTSFAwareRequired()) {
1065 // TODO(nona): Implement this function.
1070 gfx::Rect RenderWidgetHostViewWin::GetCaretBounds() const {
1071 if (!base::win::IsTSFAwareRequired()) {
1073 return gfx::Rect(0, 0, 0, 0);
1075 RECT tmp_rect = caret_rect_.ToRECT();
1076 ClientToScreen(&tmp_rect);
1077 return gfx::Rect(tmp_rect);
1080 bool RenderWidgetHostViewWin::GetCompositionCharacterBounds(
1081 uint32 index, gfx::Rect* rect) const {
1082 if (!base::win::IsTSFAwareRequired()) {
1087 if (index >= composition_character_bounds_.size())
1089 RECT rec = composition_character_bounds_[index].ToRECT();
1090 ClientToScreen(&rec);
1091 *rect = gfx::Rect(rec);
1095 bool RenderWidgetHostViewWin::HasCompositionText() const {
1096 if (!base::win::IsTSFAwareRequired()) {
1100 // TODO(nona): Implement this function.
1105 bool RenderWidgetHostViewWin::GetTextRange(gfx::Range* range) const {
1106 if (!base::win::IsTSFAwareRequired()) {
1110 range->set_start(selection_text_offset_);
1111 range->set_end(selection_text_offset_ + selection_text_.length());
1115 bool RenderWidgetHostViewWin::GetCompositionTextRange(gfx::Range* range) const {
1116 if (!base::win::IsTSFAwareRequired()) {
1120 // TODO(nona): Implement this function.
1125 bool RenderWidgetHostViewWin::GetSelectionRange(gfx::Range* range) const {
1126 if (!base::win::IsTSFAwareRequired()) {
1130 range->set_start(selection_range_.start());
1131 range->set_end(selection_range_.end());
1135 bool RenderWidgetHostViewWin::SetSelectionRange(const gfx::Range& range) {
1136 if (!base::win::IsTSFAwareRequired()) {
1140 // TODO(nona): Implement this function.
1145 bool RenderWidgetHostViewWin::DeleteRange(const gfx::Range& range) {
1146 if (!base::win::IsTSFAwareRequired()) {
1150 // TODO(nona): Implement this function.
1155 bool RenderWidgetHostViewWin::GetTextFromRange(const gfx::Range& range,
1156 string16* text) const {
1157 if (!base::win::IsTSFAwareRequired()) {
1161 gfx::Range selection_text_range(selection_text_offset_,
1162 selection_text_offset_ + selection_text_.length());
1163 if (!selection_text_range.Contains(range)) {
1167 if (selection_text_range.EqualsIgnoringDirection(range)) {
1168 *text = selection_text_;
1170 *text = selection_text_.substr(
1171 range.GetMin() - selection_text_offset_,
1177 void RenderWidgetHostViewWin::OnInputMethodChanged() {
1178 if (!base::win::IsTSFAwareRequired()) {
1182 // TODO(nona): Implement this function.
1186 bool RenderWidgetHostViewWin::ChangeTextDirectionAndLayoutAlignment(
1187 base::i18n::TextDirection direction) {
1188 if (!base::win::IsTSFAwareRequired()) {
1192 // TODO(nona): Implement this function.
1197 void RenderWidgetHostViewWin::ExtendSelectionAndDelete(
1200 if (!base::win::IsTSFAwareRequired()) {
1204 if (!render_widget_host_)
1206 render_widget_host_->ExtendSelectionAndDelete(before, after);
1209 void RenderWidgetHostViewWin::EnsureCaretInRect(const gfx::Rect& rect) {
1210 // TODO(nona): Implement this function.
1214 ///////////////////////////////////////////////////////////////////////////////
1215 // RenderWidgetHostViewWin, private:
1217 LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) {
1218 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCreate");
1219 // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale
1220 // of a browser process.
1221 OnInputLangChange(0, 0);
1222 // Marks that window as supporting mouse-wheel messages rerouting so it is
1223 // scrolled when under the mouse pointer even if inactive.
1224 props_.push_back(ui::SetWindowSupportsRerouteMouseWheel(m_hWnd));
1226 WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION);
1228 UpdateDesiredTouchMode();
1234 void RenderWidgetHostViewWin::OnActivate(UINT action, BOOL minimized,
1236 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnActivate");
1237 // If the container is a popup, clicking elsewhere on screen should close the
1239 if (close_on_deactivate_ && action == WA_INACTIVE) {
1240 // Send a windows message so that any derived classes
1241 // will get a change to override the default handling
1242 SendMessage(WM_CANCELMODE);
1246 void RenderWidgetHostViewWin::OnDestroy() {
1247 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnDestroy");
1248 DetachPluginsHelper(m_hWnd);
1252 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
1253 IsTouchWindow(m_hWnd, NULL)) {
1254 UnregisterTouchWindow(m_hWnd);
1257 CleanupCompositorWindow();
1259 WTSUnRegisterSessionNotification(m_hWnd);
1262 TrackMouseLeave(false);
1265 void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) {
1266 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnPaint");
1268 // Grab the region to paint before creation of paint_dc since it clears the
1270 base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0));
1271 GetUpdateRgn(damage_region, FALSE);
1273 CPaintDC paint_dc(m_hWnd);
1275 if (!render_widget_host_)
1278 DCHECK(render_widget_host_->GetProcess()->HasConnection());
1280 // If the GPU process is rendering to a child window, compositing is
1281 // already triggered by damage to compositor_host_window_, so all we need to
1282 // do here is clear borders during resize.
1283 if (compositor_host_window_ &&
1284 render_widget_host_->is_accelerated_compositing_active()) {
1285 RECT host_rect, child_rect;
1286 GetClientRect(&host_rect);
1287 if (::GetClientRect(compositor_host_window_, &child_rect) &&
1288 (child_rect.right < host_rect.right ||
1289 child_rect.bottom < host_rect.bottom)) {
1290 paint_dc.FillRect(&host_rect,
1291 reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
1296 if (accelerated_surface_.get() &&
1297 render_widget_host_->is_accelerated_compositing_active()) {
1298 AcceleratedPaint(paint_dc.m_hDC);
1302 about_to_validate_and_paint_ = true;
1303 BackingStoreWin* backing_store = static_cast<BackingStoreWin*>(
1304 render_widget_host_->GetBackingStore(true));
1306 // We initialize |paint_dc| (and thus call BeginPaint()) after calling
1307 // GetBackingStore(), so that if it updates the invalid rect we'll catch the
1308 // changes and repaint them.
1309 about_to_validate_and_paint_ = false;
1311 if (compositor_host_window_ && hide_compositor_window_at_next_paint_) {
1312 ::ShowWindow(compositor_host_window_, SW_HIDE);
1313 hide_compositor_window_at_next_paint_ = false;
1316 gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint);
1317 if (damaged_rect.IsEmpty())
1320 if (backing_store) {
1321 gfx::Rect bitmap_rect(gfx::Point(),
1322 gfx::win::DIPToScreenSize(backing_store->size()));
1324 bool manage_colors = BackingStoreWin::ColorManagementEnabled();
1326 SetICMMode(paint_dc.m_hDC, ICM_ON);
1328 // Blit only the damaged regions from the backing store.
1329 DWORD data_size = GetRegionData(damage_region, 0, NULL);
1330 scoped_ptr<char[]> region_data_buf;
1331 RGNDATA* region_data = NULL;
1332 RECT* region_rects = NULL;
1335 region_data_buf.reset(new char[data_size]);
1336 region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
1337 region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
1338 data_size = GetRegionData(damage_region, data_size, region_data);
1342 // Grabbing the damaged regions failed, fake with the whole rect.
1343 data_size = sizeof(RGNDATAHEADER) + sizeof(RECT);
1344 region_data_buf.reset(new char[data_size]);
1345 region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
1346 region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
1347 region_data->rdh.nCount = 1;
1348 region_rects[0] = damaged_rect.ToRECT();
1351 for (DWORD i = 0; i < region_data->rdh.nCount; ++i) {
1352 gfx::Rect paint_rect =
1353 gfx::IntersectRects(bitmap_rect, gfx::Rect(region_rects[i]));
1354 if (!paint_rect.IsEmpty()) {
1355 BitBlt(paint_dc.m_hDC,
1359 paint_rect.height(),
1360 backing_store->hdc(),
1368 SetICMMode(paint_dc.m_hDC, ICM_OFF);
1370 // Fill the remaining portion of the damaged_rect with the background
1371 if (damaged_rect.right() > bitmap_rect.right()) {
1373 r.left = std::max(bitmap_rect.right(), damaged_rect.x());
1374 r.right = damaged_rect.right();
1375 r.top = damaged_rect.y();
1376 r.bottom = std::min(bitmap_rect.bottom(), damaged_rect.bottom());
1377 DrawBackground(r, &paint_dc);
1379 if (damaged_rect.bottom() > bitmap_rect.bottom()) {
1381 r.left = damaged_rect.x();
1382 r.right = damaged_rect.right();
1383 r.top = std::max(bitmap_rect.bottom(), damaged_rect.y());
1384 r.bottom = damaged_rect.bottom();
1385 DrawBackground(r, &paint_dc);
1387 if (!whiteout_start_time_.is_null()) {
1388 TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_;
1389 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
1391 // Reset the start time to 0 so that we start recording again the next
1392 // time the backing store is NULL...
1393 whiteout_start_time_ = TimeTicks();
1395 if (!web_contents_switch_paint_time_.is_null()) {
1396 TimeDelta web_contents_switch_paint_duration = TimeTicks::Now() -
1397 web_contents_switch_paint_time_;
1398 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
1399 web_contents_switch_paint_duration);
1400 // Reset contents_switch_paint_time_ to 0 so future tab selections are
1402 web_contents_switch_paint_time_ = TimeTicks();
1405 software_latency_info_.AddLatencyNumber(
1406 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
1407 render_widget_host_->FrameSwapped(software_latency_info_);
1408 software_latency_info_.Clear();
1410 DrawBackground(paint_dc.m_ps.rcPaint, &paint_dc);
1411 if (whiteout_start_time_.is_null())
1412 whiteout_start_time_ = TimeTicks::Now();
1416 void RenderWidgetHostViewWin::DrawBackground(const RECT& dirty_rect,
1418 if (!background_.empty()) {
1419 gfx::Rect dirty_area(dirty_rect);
1420 gfx::Canvas canvas(dirty_area.size(), 1.0f, true);
1421 canvas.Translate(-dirty_area.OffsetFromOrigin());
1423 gfx::Rect dc_rect(dc->m_ps.rcPaint);
1424 // TODO(pkotwicz): Fix |background_| such that it is an ImageSkia.
1425 canvas.TileImageInt(gfx::ImageSkia::CreateFrom1xBitmap(background_),
1426 0, 0, dc_rect.width(), dc_rect.height());
1428 skia::DrawToNativeContext(canvas.sk_canvas(), *dc, dirty_area.x(),
1429 dirty_area.y(), NULL);
1431 HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
1432 dc->FillRect(&dirty_rect, white_brush);
1436 void RenderWidgetHostViewWin::OnNCPaint(HRGN update_region) {
1437 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNCPaint");
1438 // Do nothing. This suppresses the resize corner that Windows would
1439 // otherwise draw for us.
1442 void RenderWidgetHostViewWin::SetClickthroughRegion(SkRegion* region) {
1443 transparent_region_.reset(region);
1446 LRESULT RenderWidgetHostViewWin::OnNCHitTest(const CPoint& point) {
1447 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNCHitTest");
1450 if (transparent_region_.get() &&
1451 transparent_region_->contains(point.x - rc.left, point.y - rc.top)) {
1452 SetMsgHandled(TRUE);
1453 return HTTRANSPARENT;
1455 SetMsgHandled(FALSE);
1459 LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) {
1460 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnEraseBkgnd");
1464 LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code,
1465 UINT mouse_message_id) {
1466 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSetCursor");
1467 UpdateCursorIfOverSelf();
1471 void RenderWidgetHostViewWin::OnSetFocus(HWND window) {
1472 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSetFocus");
1473 if (!render_widget_host_)
1476 if (GetBrowserAccessibilityManager())
1477 GetBrowserAccessibilityManager()->GotFocus(pointer_down_context_);
1479 render_widget_host_->GotFocus();
1480 render_widget_host_->SetActive(true);
1482 if (base::win::IsTSFAwareRequired())
1483 ui::TSFBridge::GetInstance()->SetFocusedClient(m_hWnd, this);
1486 void RenderWidgetHostViewWin::OnKillFocus(HWND window) {
1487 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnKillFocus");
1488 if (!render_widget_host_)
1491 render_widget_host_->SetActive(false);
1492 render_widget_host_->Blur();
1494 last_touch_location_ = gfx::Point(-1, -1);
1496 if (base::win::IsTSFAwareRequired())
1497 ui::TSFBridge::GetInstance()->RemoveFocusedClient(this);
1500 void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) {
1501 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCaptureChanged");
1502 if (render_widget_host_)
1503 render_widget_host_->LostCapture();
1504 pointer_down_context_ = false;
1507 void RenderWidgetHostViewWin::OnCancelMode() {
1508 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCancelMode");
1509 if (render_widget_host_)
1510 render_widget_host_->LostCapture();
1512 if ((is_fullscreen_ || close_on_deactivate_) &&
1513 !weak_factory_.HasWeakPtrs()) {
1514 // Dismiss popups and menus. We do this asynchronously to avoid changing
1515 // activation within this callstack, which may interfere with another window
1516 // being activated. We can synchronously hide the window, but we need to
1517 // not change activation while doing so.
1518 SetWindowPos(NULL, 0, 0, 0, 0,
1519 SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
1520 SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
1521 base::MessageLoop::current()->PostTask(
1523 base::Bind(&RenderWidgetHostViewWin::ShutdownHost,
1524 weak_factory_.GetWeakPtr()));
1528 void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set,
1529 HKL input_language_id) {
1530 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnInputLangChange");
1531 // Send the given Locale ID to the IMM32Manager object and retrieves whether
1532 // or not the current input context has IMEs.
1533 // If the current input context has IMEs, a browser process has to send a
1534 // request to a renderer process that it needs status messages about
1535 // the focused edit control from the renderer process.
1536 // On the other hand, if the current input context does not have IMEs, the
1537 // browser process also has to send a request to the renderer process that
1538 // it does not need the status messages any longer.
1539 // To minimize the number of this notification request, we should check if
1540 // the browser process is actually retrieving the status messages (this
1541 // state is stored in ime_notification_) and send a request only if the
1542 // browser process has to update this status, its details are listed below:
1543 // * If a browser process is not retrieving the status messages,
1544 // (i.e. ime_notification_ == false),
1545 // send this request only if the input context does have IMEs,
1546 // (i.e. ime_status == true);
1547 // When it successfully sends the request, toggle its notification status,
1548 // (i.e.ime_notification_ = !ime_notification_ = true).
1549 // * If a browser process is retrieving the status messages
1550 // (i.e. ime_notification_ == true),
1551 // send this request only if the input context does not have IMEs,
1552 // (i.e. ime_status == false).
1553 // When it successfully sends the request, toggle its notification status,
1554 // (i.e.ime_notification_ = !ime_notification_ = false).
1555 // To analyze the above actions, we can optimize them into the ones
1557 // 1 Sending a request only if ime_status_ != ime_notification_, and;
1558 // 2 Copying ime_status to ime_notification_ if it sends the request
1559 // successfully (because Action 1 shows ime_status = !ime_notification_.)
1560 bool ime_status = imm32_manager_->SetInputLanguage();
1561 if (ime_status != ime_notification_) {
1562 if (render_widget_host_) {
1563 render_widget_host_->SetInputMethodActive(ime_status);
1564 ime_notification_ = ime_status;
1567 // Call DefWindowProc() for consistency with other Chrome windows.
1568 // TODO(hbono): This is a speculative fix for Bug 36354 and this code may be
1569 // reverted if it does not fix it.
1570 SetMsgHandled(FALSE);
1573 void RenderWidgetHostViewWin::OnThemeChanged() {
1574 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnThemeChanged");
1575 if (!render_widget_host_)
1577 render_widget_host_->Send(new ViewMsg_ThemeChanged(
1578 render_widget_host_->GetRoutingID()));
1581 LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) {
1582 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNotify");
1583 if (tooltip_hwnd_ == NULL)
1586 switch (header->code) {
1587 case TTN_GETDISPINFO: {
1588 NMTTDISPINFOW* tooltip_info = reinterpret_cast<NMTTDISPINFOW*>(header);
1589 tooltip_info->szText[0] = L'\0';
1590 tooltip_info->lpszText = const_cast<WCHAR*>(tooltip_text_.c_str());
1592 tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels);
1593 SetMsgHandled(TRUE);
1597 tooltip_showing_ = false;
1598 SetMsgHandled(TRUE);
1601 // Tooltip shouldn't be shown when the mouse is locked.
1602 DCHECK(!mouse_locked_);
1603 tooltip_showing_ = true;
1604 SetMsgHandled(TRUE);
1610 LRESULT RenderWidgetHostViewWin::OnImeSetContext(
1611 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
1612 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeSetContext");
1613 if (!render_widget_host_)
1616 // We need status messages about the focused input control from a
1617 // renderer process when:
1618 // * the current input context has IMEs, and;
1619 // * an application is activated.
1620 // This seems to tell we should also check if the current input context has
1621 // IMEs before sending a request, however, this WM_IME_SETCONTEXT is
1622 // fortunately sent to an application only while the input context has IMEs.
1623 // Therefore, we just start/stop status messages according to the activation
1624 // status of this application without checks.
1625 bool activated = (wparam == TRUE);
1626 if (render_widget_host_) {
1627 render_widget_host_->SetInputMethodActive(activated);
1628 ime_notification_ = activated;
1631 if (ime_notification_)
1632 imm32_manager_->CreateImeWindow(m_hWnd);
1634 imm32_manager_->CleanupComposition(m_hWnd);
1635 return imm32_manager_->SetImeWindowStyle(
1636 m_hWnd, message, wparam, lparam, &handled);
1639 LRESULT RenderWidgetHostViewWin::OnImeStartComposition(
1640 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
1641 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeStartComposition");
1642 if (!render_widget_host_)
1645 // Reset the composition status and create IME windows.
1646 imm32_manager_->CreateImeWindow(m_hWnd);
1647 imm32_manager_->ResetComposition(m_hWnd);
1648 // When the focus is on an element that does not draw composition by itself
1649 // (i.e., PPAPI plugin not handling IME), let IME to draw the text. Otherwise
1650 // we have to prevent WTL from calling ::DefWindowProc() because the function
1651 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to
1652 // over-write the position of IME windows.
1653 handled = (can_compose_inline_ ? TRUE : FALSE);
1657 LRESULT RenderWidgetHostViewWin::OnImeComposition(
1658 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
1659 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeComposition");
1660 if (!render_widget_host_)
1663 // At first, update the position of the IME window.
1664 imm32_manager_->UpdateImeWindow(m_hWnd);
1666 // ui::CompositionUnderline should be identical to
1667 // WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely.
1668 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
1669 sizeof(WebKit::WebCompositionUnderline),
1670 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
1672 // Retrieve the result string and its attributes of the ongoing composition
1673 // and send it to a renderer process.
1674 ui::CompositionText composition;
1675 if (imm32_manager_->GetResult(m_hWnd, lparam, &composition.text)) {
1676 render_widget_host_->ImeConfirmComposition(
1677 composition.text, gfx::Range::InvalidRange(), false);
1678 imm32_manager_->ResetComposition(m_hWnd);
1679 // Fall though and try reading the composition string.
1680 // Japanese IMEs send a message containing both GCS_RESULTSTR and
1681 // GCS_COMPSTR, which means an ongoing composition has been finished
1682 // by the start of another composition.
1684 // Retrieve the composition string and its attributes of the ongoing
1685 // composition and send it to a renderer process.
1686 if (imm32_manager_->GetComposition(m_hWnd, lparam, &composition)) {
1687 // TODO(suzhe): due to a bug of webkit, we can't use selection range with
1688 // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788
1689 composition.selection = gfx::Range(composition.selection.end());
1691 // TODO(suzhe): convert both renderer_host and renderer to use
1692 // ui::CompositionText.
1693 const std::vector<WebKit::WebCompositionUnderline>& underlines =
1694 reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>(
1695 composition.underlines);
1696 render_widget_host_->ImeSetComposition(
1697 composition.text, underlines,
1698 composition.selection.start(), composition.selection.end());
1700 // We have to prevent WTL from calling ::DefWindowProc() because we do not
1701 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages.
1703 if (!can_compose_inline_) {
1704 // When the focus is on an element that does not draw composition by itself
1705 // (i.e., PPAPI plugin not handling IME), let IME to draw the text, which
1706 // is the default behavior of DefWindowProc. Note, however, even in this
1707 // case we don't want GCS_RESULTSTR to be converted to WM_IME_CHAR messages.
1708 // Thus we explicitly drop the flag.
1709 return ::DefWindowProc(m_hWnd, message, wparam, lparam & ~GCS_RESULTSTR);
1714 LRESULT RenderWidgetHostViewWin::OnImeEndComposition(
1715 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
1716 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeEndComposition");
1717 if (!render_widget_host_)
1720 if (imm32_manager_->is_composing()) {
1721 // A composition has been ended while there is an ongoing composition,
1722 // i.e. the ongoing composition has been canceled.
1723 // We need to reset the composition status both of the IMM32Manager object
1724 // and of the renderer process.
1725 render_widget_host_->ImeCancelComposition();
1726 imm32_manager_->ResetComposition(m_hWnd);
1728 imm32_manager_->DestroyImeWindow(m_hWnd);
1729 // Let WTL call ::DefWindowProc() and release its resources.
1734 LRESULT RenderWidgetHostViewWin::OnImeRequest(
1735 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
1736 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeRequest");
1737 if (!render_widget_host_) {
1742 // Should not receive WM_IME_REQUEST message, if IME is disabled.
1743 if (text_input_type_ == ui::TEXT_INPUT_TYPE_NONE ||
1744 text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) {
1750 case IMR_RECONVERTSTRING:
1751 return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam));
1752 case IMR_DOCUMENTFEED:
1753 return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam));
1754 case IMR_QUERYCHARPOSITION:
1755 return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam));
1762 LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam,
1763 LPARAM lparam, BOOL& handled) {
1764 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnMouseEvent");
1767 if (message == WM_MOUSELEAVE)
1768 ignore_mouse_movement_ = true;
1770 if (mouse_locked_) {
1771 HandleLockedMouseEvent(message, wparam, lparam);
1772 MoveCursorToCenterIfNecessary();
1776 if (::IsWindow(tooltip_hwnd_)) {
1777 // Forward mouse events through to the tooltip window
1780 msg.message = message;
1781 msg.wParam = wparam;
1782 msg.lParam = lparam;
1783 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, NULL,
1784 reinterpret_cast<LPARAM>(&msg));
1787 // Due to a bug in Windows, the simulated mouse events for a touch event
1788 // outside our bounds are delivered to us if we were previously focused
1789 // causing crbug.com/159982. As a workaround, we check if this event is a
1790 // simulated mouse event outside our bounds, and if so, we send it to the
1792 if ((message == WM_LBUTTONDOWN || message == WM_LBUTTONUP) &&
1793 ui::IsMouseEventFromTouch(message)) {
1794 CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
1795 ClientToScreen(&cursor_pos);
1796 if (!GetPixelBounds().Contains(cursor_pos.x, cursor_pos.y)) {
1797 HWND window = WindowFromPoint(cursor_pos);
1799 LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0,
1800 MAKELPARAM(cursor_pos.x, cursor_pos.y));
1801 const bool in_client_area = (nc_hit_result == HTCLIENT);
1803 if (message == WM_LBUTTONDOWN)
1804 event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
1806 event_type = in_client_area ? WM_LBUTTONUP : WM_NCLBUTTONUP;
1808 // Convert the coordinates to the target window.
1810 ::GetWindowRect(window, &window_bounds);
1811 int window_x = cursor_pos.x - window_bounds.left;
1812 int window_y = cursor_pos.y - window_bounds.top;
1813 if (in_client_area) {
1814 ::PostMessage(window, event_type, wparam,
1815 MAKELPARAM(window_x, window_y));
1817 ::PostMessage(window, event_type, nc_hit_result,
1818 MAKELPARAM(cursor_pos.x, cursor_pos.y));
1825 // TODO(jcampan): I am not sure if we should forward the message to the
1826 // WebContentsImpl first in the case of popups. If we do, we would need to
1827 // convert the click from the popup window coordinates to the WebContentsImpl'
1828 // window coordinates. For now we don't forward the message in that case to
1829 // address bug #907474.
1830 // Note: GetParent() on popup windows returns the top window and not the
1831 // parent the window was created with (the parent and the owner of the popup
1832 // is the first non-child view of the view that was specified to the create
1833 // call). So the WebContentsImpl's window would have to be specified to the
1834 // RenderViewHostHWND as there is no way to retrieve it from the HWND.
1836 // Don't forward if the container is a popup or fullscreen widget.
1837 if (!is_fullscreen_ && !close_on_deactivate_) {
1839 case WM_LBUTTONDOWN:
1840 case WM_MBUTTONDOWN:
1841 case WM_RBUTTONDOWN:
1842 // Finish the ongoing composition whenever a mouse click happens.
1843 // It matches IE's behavior.
1844 if (base::win::IsTSFAwareRequired()) {
1845 ui::TSFBridge::GetInstance()->CancelComposition();
1847 imm32_manager_->CleanupComposition(m_hWnd);
1851 case WM_MOUSELEAVE: {
1852 // Give the WebContentsImpl first crack at the message. It may want to
1853 // prevent forwarding to the renderer if some higher level browser
1854 // functionality is invoked.
1855 LPARAM parent_msg_lparam = lparam;
1856 if (message != WM_MOUSELEAVE) {
1857 // For the messages except WM_MOUSELEAVE, before forwarding them to
1858 // parent window, we should adjust cursor position from client
1859 // coordinates in current window to client coordinates in its parent
1861 CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
1862 ClientToScreen(&cursor_pos);
1863 GetParent().ScreenToClient(&cursor_pos);
1864 parent_msg_lparam = MAKELPARAM(cursor_pos.x, cursor_pos.y);
1866 if (SendMessage(GetParent(), message, wparam, parent_msg_lparam) != 0) {
1867 TRACE_EVENT0("browser", "EarlyOut_SentToParent");
1874 if (message == WM_LBUTTONDOWN && pointer_down_context_ &&
1875 GetBrowserAccessibilityManager()) {
1876 GetBrowserAccessibilityManager()->GotMouseDown();
1879 if (message == WM_LBUTTONUP && ui::IsMouseEventFromTouch(message) &&
1880 base::win::IsMetroProcess())
1881 pointer_down_context_ = false;
1883 ForwardMouseEventToRenderer(message, wparam, lparam);
1887 LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam,
1888 LPARAM lparam, BOOL& handled) {
1889 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnKeyEvent");
1892 // When Escape is pressed, force fullscreen windows to close if necessary.
1893 if ((message == WM_KEYDOWN || message == WM_KEYUP) && wparam == VK_ESCAPE) {
1894 if (is_fullscreen_) {
1895 SendMessage(WM_CANCELMODE);
1900 // If we are a pop-up, forward tab related messages to our parent HWND, so
1901 // that we are dismissed appropriately and so that the focus advance in our
1903 // TODO(jcampan): http://b/issue?id=1192881 Could be abstracted in the
1905 if (close_on_deactivate_ &&
1906 (((message == WM_KEYDOWN || message == WM_KEYUP) && (wparam == VK_TAB)) ||
1907 (message == WM_CHAR && wparam == L'\t'))) {
1908 // First close the pop-up.
1909 SendMessage(WM_CANCELMODE);
1910 // Then move the focus by forwarding the tab key to the parent.
1911 return ::SendMessage(GetParent(), message, wparam, lparam);
1914 if (!render_widget_host_)
1917 // Bug 1845: we need to update the text direction when a user releases
1918 // either a right-shift key or a right-control key after pressing both of
1919 // them. So, we just update the text direction while a user is pressing the
1920 // keys, and we notify the text direction when a user releases either of them.
1921 // Bug 9718: http://crbug.com/9718 To investigate IE and notepad, this
1922 // shortcut is enabled only on a PC having RTL keyboard layouts installed.
1923 // We should emulate them.
1924 if (ui::IMM32Manager::IsRTLKeyboardLayoutInstalled()) {
1925 if (message == WM_KEYDOWN) {
1926 if (wparam == VK_SHIFT) {
1927 base::i18n::TextDirection dir;
1928 if (ui::IMM32Manager::IsCtrlShiftPressed(&dir)) {
1929 render_widget_host_->UpdateTextDirection(
1930 dir == base::i18n::RIGHT_TO_LEFT ?
1931 WebKit::WebTextDirectionRightToLeft :
1932 WebKit::WebTextDirectionLeftToRight);
1934 } else if (wparam != VK_CONTROL) {
1935 // Bug 9762: http://crbug.com/9762 A user pressed a key except shift
1936 // and control keys.
1937 // When a user presses a key while he/she holds control and shift keys,
1938 // we cancel sending an IPC message in NotifyTextDirection() below and
1939 // ignore succeeding UpdateTextDirection() calls while we call
1940 // NotifyTextDirection().
1941 // To cancel it, this call set a flag that prevents sending an IPC
1942 // message in NotifyTextDirection() only if we are going to send it.
1943 // It is harmless to call this function if we aren't going to send it.
1944 render_widget_host_->CancelUpdateTextDirection();
1946 } else if (message == WM_KEYUP &&
1947 (wparam == VK_SHIFT || wparam == VK_CONTROL)) {
1948 // We send an IPC message only if we need to update the text direction.
1949 render_widget_host_->NotifyTextDirection();
1953 // Special processing for enter key: When user hits enter in omnibox
1954 // we change focus to render host after the navigation, so repeat WM_KEYDOWNs
1955 // and WM_KEYUP are going to render host, despite being initiated in other
1956 // window. This code filters out these messages.
1957 bool ignore_keyboard_event = false;
1958 if (wparam == VK_RETURN) {
1959 if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN) {
1960 if (KF_REPEAT & HIWORD(lparam)) {
1961 // this is a repeated key
1962 if (!capture_enter_key_)
1963 ignore_keyboard_event = true;
1965 capture_enter_key_ = true;
1967 } else if (message == WM_KEYUP || message == WM_SYSKEYUP) {
1968 if (!capture_enter_key_)
1969 ignore_keyboard_event = true;
1970 capture_enter_key_ = false;
1972 // Ignore all other keyboard events for the enter key if not captured.
1973 if (!capture_enter_key_)
1974 ignore_keyboard_event = true;
1978 if (render_widget_host_ && !ignore_keyboard_event) {
1979 MSG msg = { m_hWnd, message, wparam, lparam };
1980 render_widget_host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(msg));
1986 LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam,
1987 LPARAM lparam, BOOL& handled) {
1988 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnWheelEvent");
1989 // Forward the mouse-wheel message to the window under the mouse if it belongs
1991 if (message == WM_MOUSEWHEEL &&
1992 ui::RerouteMouseWheel(m_hWnd, wparam, lparam)) {
1997 // We get mouse wheel/scroll messages even if we are not in the foreground.
1998 // So here we check if we have any owned popup windows in the foreground and
2000 if (m_hWnd != GetForegroundWindow()) {
2001 HWND toplevel_hwnd = ::GetAncestor(m_hWnd, GA_ROOT);
2003 GetCurrentThreadId(),
2005 reinterpret_cast<LPARAM>(toplevel_hwnd));
2008 if (render_widget_host_) {
2009 WebKit::WebMouseWheelEvent wheel_event =
2010 WebMouseWheelEventBuilder::Build(m_hWnd, message, wparam, lparam);
2011 float scale = gfx::win::GetDeviceScaleFactor();
2012 wheel_event.x /= scale;
2013 wheel_event.y /= scale;
2014 wheel_event.deltaX /= scale;
2015 wheel_event.deltaY /= scale;
2017 render_widget_host_->ForwardWheelEvent(wheel_event);
2023 WebTouchState::WebTouchState(const RenderWidgetHostViewWin* window)
2028 size_t WebTouchState::UpdateTouchPoints(
2029 TOUCHINPUT* points, size_t count) {
2030 // First we reset all touch event state. This involves removing any released
2031 // touchpoints and marking the rest as stationary. After that we go through
2032 // and alter/add any touchpoints (from the touch input buffer) that we can
2033 // coalesce into a single message. The return value is the number of consumed
2035 WebKit::WebTouchPoint* point = touch_event_.touches;
2036 WebKit::WebTouchPoint* end = point + touch_event_.touchesLength;
2037 while (point < end) {
2038 if (point->state == WebKit::WebTouchPoint::StateReleased) {
2040 --touch_event_.touchesLength;
2042 point->state = WebKit::WebTouchPoint::StateStationary;
2046 touch_event_.changedTouchesLength = 0;
2047 touch_event_.modifiers = content::EventFlagsToWebEventModifiers(
2048 ui::GetModifiersFromKeyState());
2050 // Consume all events of the same type and add them to the changed list.
2052 for (size_t i = 0; i < count; ++i) {
2053 unsigned int mapped_id = GetMappedTouch(points[i].dwID);
2055 WebKit::WebTouchPoint* point = NULL;
2056 for (unsigned j = 0; j < touch_event_.touchesLength; ++j) {
2057 if (static_cast<DWORD>(touch_event_.touches[j].id) == mapped_id) {
2058 point = &touch_event_.touches[j];
2063 // Use a move instead if we see a down on a point we already have.
2064 int type = GetTouchType(points[i]);
2065 if (point && type == TOUCHEVENTF_DOWN)
2066 SetTouchType(&points[i], TOUCHEVENTF_MOVE);
2068 // Stop processing when the event type changes.
2069 if (touch_event_.changedTouchesLength && type != last_type)
2072 touch_event_.timeStampSeconds = points[i].dwTime / 1000.0;
2076 case TOUCHEVENTF_DOWN: {
2077 if (!(point = AddTouchPoint(&points[i])))
2079 touch_event_.type = WebKit::WebInputEvent::TouchStart;
2083 case TOUCHEVENTF_UP: {
2084 if (!point) // Just throw away a stray up.
2086 point->state = WebKit::WebTouchPoint::StateReleased;
2087 UpdateTouchPoint(point, &points[i]);
2088 touch_event_.type = WebKit::WebInputEvent::TouchEnd;
2092 case TOUCHEVENTF_MOVE: {
2094 point->state = WebKit::WebTouchPoint::StateMoved;
2095 // Don't update the message if the point didn't really move.
2096 if (UpdateTouchPoint(point, &points[i]))
2098 touch_event_.type = WebKit::WebInputEvent::TouchMove;
2099 } else if (touch_event_.changedTouchesLength) {
2100 RemoveExpiredMappings();
2101 // Can't add a point if we're already handling move events.
2104 // Treat a move with no existing point as a down.
2105 if (!(point = AddTouchPoint(&points[i])))
2107 last_type = TOUCHEVENTF_DOWN;
2108 SetTouchType(&points[i], TOUCHEVENTF_DOWN);
2109 touch_event_.type = WebKit::WebInputEvent::TouchStart;
2118 touch_event_.changedTouches[touch_event_.changedTouchesLength++] = *point;
2121 RemoveExpiredMappings();
2125 void WebTouchState::RemoveExpiredMappings() {
2126 WebKit::WebTouchPoint* point = touch_event_.touches;
2127 WebKit::WebTouchPoint* end = point + touch_event_.touchesLength;
2128 for (; point < end; ++point) {
2129 if (point->state == WebKit::WebTouchPoint::StateReleased)
2130 id_generator_.ReleaseGeneratedID(point->id);
2135 bool WebTouchState::ReleaseTouchPoints() {
2136 if (touch_event_.touchesLength == 0)
2138 // Mark every active touchpoint as released.
2139 touch_event_.type = WebKit::WebInputEvent::TouchEnd;
2140 touch_event_.changedTouchesLength = touch_event_.touchesLength;
2141 for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) {
2142 touch_event_.touches[i].state = WebKit::WebTouchPoint::StateReleased;
2143 touch_event_.changedTouches[i].state =
2144 WebKit::WebTouchPoint::StateReleased;
2150 WebKit::WebTouchPoint* WebTouchState::AddTouchPoint(
2151 TOUCHINPUT* touch_input) {
2152 DCHECK(touch_event_.touchesLength <
2153 WebKit::WebTouchEvent::touchesLengthCap);
2154 if (touch_event_.touchesLength >=
2155 WebKit::WebTouchEvent::touchesLengthCap)
2157 WebKit::WebTouchPoint* point =
2158 &touch_event_.touches[touch_event_.touchesLength++];
2159 point->state = WebKit::WebTouchPoint::StatePressed;
2160 point->id = GetMappedTouch(touch_input->dwID);
2161 UpdateTouchPoint(point, touch_input);
2165 bool WebTouchState::UpdateTouchPoint(
2166 WebKit::WebTouchPoint* touch_point,
2167 TOUCHINPUT* touch_input) {
2169 TOUCH_COORD_TO_PIXEL(touch_input->x) /
2170 gfx::win::GetUndocumentedDPITouchScale(),
2171 TOUCH_COORD_TO_PIXEL(touch_input->y) /
2172 gfx::win::GetUndocumentedDPITouchScale());
2175 if (touch_input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) {
2176 // Some touch drivers send a contact area of "-1", yet flag it as valid.
2177 radius_x = std::max(1,
2178 static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cxContact) /
2179 gfx::win::GetUndocumentedDPITouchScale()));
2180 radius_y = std::max(1,
2181 static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cyContact) /
2182 gfx::win::GetUndocumentedDPITouchScale()));
2185 // Detect and exclude stationary moves.
2186 if (GetTouchType(*touch_input) == TOUCHEVENTF_MOVE &&
2187 touch_point->screenPosition.x == coordinates.x &&
2188 touch_point->screenPosition.y == coordinates.y &&
2189 touch_point->radiusX == radius_x &&
2190 touch_point->radiusY == radius_y) {
2191 touch_point->state = WebKit::WebTouchPoint::StateStationary;
2195 touch_point->screenPosition.x = coordinates.x;
2196 touch_point->screenPosition.y = coordinates.y;
2197 window_->ScreenToClient(&coordinates);
2198 static float scale = gfx::win::GetDeviceScaleFactor();
2199 touch_point->position.x = coordinates.x / scale;
2200 touch_point->position.y = coordinates.y / scale;
2201 touch_point->radiusX = radius_x;
2202 touch_point->radiusY = radius_y;
2203 touch_point->force = 0;
2204 touch_point->rotationAngle = 0;
2208 // Find (or create) a mapping for _os_touch_id_.
2209 unsigned int WebTouchState::GetMappedTouch(unsigned int os_touch_id) {
2210 return id_generator_.GetGeneratedID(os_touch_id);
2213 LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam,
2214 LPARAM lparam, BOOL& handled) {
2215 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnTouchEvent");
2216 // Finish the ongoing composition whenever a touch event happens.
2217 // It matches IE's behavior.
2218 if (base::win::IsTSFAwareRequired()) {
2219 ui::TSFBridge::GetInstance()->CancelComposition();
2221 imm32_manager_->CleanupComposition(m_hWnd);
2224 // TODO(jschuh): Add support for an arbitrary number of touchpoints.
2225 size_t total = std::min(static_cast<int>(LOWORD(wparam)),
2226 static_cast<int>(WebKit::WebTouchEvent::touchesLengthCap));
2227 TOUCHINPUT points[WebKit::WebTouchEvent::touchesLengthCap];
2229 if (!total || !ui::GetTouchInputInfoWrapper((HTOUCHINPUT)lparam, total,
2230 points, sizeof(TOUCHINPUT))) {
2231 TRACE_EVENT0("browser", "EarlyOut_NothingToDo");
2235 if (total == 1 && (points[0].dwFlags & TOUCHEVENTF_DOWN)) {
2236 pointer_down_context_ = true;
2237 last_touch_location_ = gfx::Point(
2238 TOUCH_COORD_TO_PIXEL(points[0].x) /
2239 gfx::win::GetUndocumentedDPITouchScale(),
2240 TOUCH_COORD_TO_PIXEL(points[0].y) /
2241 gfx::win::GetUndocumentedDPITouchScale());
2244 bool should_forward = render_widget_host_->ShouldForwardTouchEvent() &&
2245 touch_events_enabled_;
2247 // Send a copy of the touch events on to the gesture recognizer.
2248 for (size_t start = 0; start < total;) {
2249 start += touch_state_->UpdateTouchPoints(points + start, total - start);
2250 if (should_forward) {
2251 if (touch_state_->is_changed())
2252 render_widget_host_->ForwardTouchEventWithLatencyInfo(
2253 touch_state_->touch_event(), ui::LatencyInfo());
2255 const WebKit::WebTouchEvent& touch_event = touch_state_->touch_event();
2256 base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds(
2257 touch_event.timeStampSeconds * 1000);
2258 for (size_t i = 0; i < touch_event.touchesLength; ++i) {
2259 scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
2260 gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
2261 TouchEventFromWebTouchPoint(touch_event.touches[i], timestamp),
2262 ui::ER_UNHANDLED, this));
2263 ProcessGestures(gestures.get());
2268 CloseTouchInputHandle((HTOUCHINPUT)lparam);
2273 void RenderWidgetHostViewWin::ProcessGestures(
2274 ui::GestureRecognizer::Gestures* gestures) {
2275 if ((gestures == NULL) || gestures->empty())
2277 for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin();
2278 g_it != gestures->end();
2280 ForwardGestureEventToRenderer(*g_it);
2284 LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message,
2288 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnMouseActivate");
2289 if (!render_widget_host_)
2290 return MA_NOACTIVATE;
2292 if (!IsActivatable())
2293 return MA_NOACTIVATE;
2295 HWND focus_window = GetFocus();
2296 if (!::IsWindow(focus_window) || !IsChild(focus_window)) {
2297 // We handle WM_MOUSEACTIVATE to set focus to the underlying plugin
2298 // child window. This is to ensure that keyboard events are received
2299 // by the plugin. The correct way to fix this would be send over
2300 // an event to the renderer which would then eventually send over
2301 // a setFocus call to the plugin widget. This would ensure that
2302 // the renderer (webkit) knows about the plugin widget receiving
2304 // TODO(iyengar) Do the right thing as per the above comment.
2305 POINT cursor_pos = {0};
2306 ::GetCursorPos(&cursor_pos);
2307 ::ScreenToClient(m_hWnd, &cursor_pos);
2308 HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos);
2309 if (::IsWindow(child_window) && child_window != m_hWnd) {
2310 if (gfx::GetClassName(child_window) == kWrapperNativeWindowClassName)
2311 child_window = ::GetWindow(child_window, GW_CHILD);
2313 ::SetFocus(child_window);
2314 return MA_NOACTIVATE;
2318 render_widget_host_->OnPointerEventActivate();
2322 LRESULT RenderWidgetHostViewWin::OnGestureEvent(
2323 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
2324 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGestureEvent");
2326 DCHECK(!touch_events_enabled_);
2329 GESTUREINFO gi = {sizeof(GESTUREINFO)};
2330 HGESTUREINFO gi_handle = reinterpret_cast<HGESTUREINFO>(lparam);
2331 if (!::GetGestureInfo(gi_handle, &gi)) {
2332 DWORD error = GetLastError();
2333 NOTREACHED() << "Unable to get gesture info. Error : " << error;
2337 if (gi.dwID == GID_ZOOM) {
2338 PageZoom zoom = PAGE_ZOOM_RESET;
2339 POINT zoom_center = {0};
2340 if (DecodeZoomGesture(m_hWnd, gi, &zoom, &zoom_center)) {
2342 Send(new ViewMsg_ZoomFactor(render_widget_host_->GetRoutingID(),
2343 zoom, zoom_center.x, zoom_center.y));
2345 } else if (gi.dwID == GID_PAN) {
2346 // Right now we only decode scroll gestures and we forward to the page
2347 // as scroll events.
2350 if (DecodeScrollGesture(gi, &start, &delta)) {
2352 render_widget_host_->ForwardWheelEvent(
2353 MakeFakeScrollWheelEvent(m_hWnd, start, delta));
2356 ::CloseGestureInfoHandle(gi_handle);
2360 LRESULT RenderWidgetHostViewWin::OnMoveOrSize(
2361 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
2362 // Reset the cliping rectangle if the mouse is locked.
2363 if (mouse_locked_) {
2365 GetWindowRect(&rect);
2366 ::ClipCursor(&rect);
2371 void RenderWidgetHostViewWin::OnAccessibilityEvents(
2372 const std::vector<AccessibilityHostMsg_EventParams>& params) {
2373 CreateBrowserAccessibilityManagerIfNeeded();
2374 GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
2377 bool RenderWidgetHostViewWin::LockMouse() {
2381 mouse_locked_ = true;
2383 // Hide the tooltip window if it is currently visible. When the mouse is
2384 // locked, no mouse message is relayed to the tooltip window, so we don't need
2385 // to worry that it will reappear.
2386 if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) {
2387 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
2388 // Sending a TTM_POP message doesn't seem to actually hide the tooltip
2389 // window, although we will receive a TTN_POP notification. As a result,
2390 // ShowWindow() is explicitly called to hide the window.
2391 ::ShowWindow(tooltip_hwnd_, SW_HIDE);
2394 // TODO(yzshen): ShowCursor(FALSE) causes SetCursorPos() to be ignored on
2396 ::ShowCursor(FALSE);
2398 move_to_center_request_.pending = false;
2399 last_mouse_position_.locked_global = last_mouse_position_.unlocked_global;
2401 // Must set the clip rectangle before MoveCursorToCenterIfNecessary()
2402 // so that if the cursor is moved it uses the clip rect set to the window
2403 // rect. Otherwise, MoveCursorToCenterIfNecessary() may move the cursor
2404 // to the center of the screen, and then we would clip to the window
2405 // rect, thus moving the cursor and causing a movement delta.
2407 GetWindowRect(&rect);
2408 ::ClipCursor(&rect);
2409 MoveCursorToCenterIfNecessary();
2414 void RenderWidgetHostViewWin::UnlockMouse() {
2418 mouse_locked_ = false;
2421 ::SetCursorPos(last_mouse_position_.unlocked_global.x(),
2422 last_mouse_position_.unlocked_global.y());
2425 if (render_widget_host_)
2426 render_widget_host_->LostMouseLock();
2429 void RenderWidgetHostViewWin::Observe(
2431 const NotificationSource& source,
2432 const NotificationDetails& details) {
2433 DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
2435 // Get the RenderProcessHost that posted this notification, and exit
2436 // if it's not the one associated with this host view.
2437 RenderProcessHost* render_process_host =
2438 Source<RenderProcessHost>(source).ptr();
2439 DCHECK(render_process_host);
2440 if (!render_widget_host_ ||
2441 render_process_host != render_widget_host_->GetProcess()) {
2445 // If it was our RenderProcessHost that posted the notification,
2446 // clear the BrowserAccessibilityManager, because the renderer is
2447 // dead and any accessibility information we have is now stale.
2448 SetBrowserAccessibilityManager(NULL);
2451 static void PaintCompositorHostWindow(HWND hWnd) {
2453 BeginPaint(hWnd, &paint);
2455 RenderWidgetHostViewWin* win = static_cast<RenderWidgetHostViewWin*>(
2456 gfx::GetWindowUserData(hWnd));
2457 // Trigger composite to rerender window.
2459 win->AcceleratedPaint(paint.hdc);
2461 EndPaint(hWnd, &paint);
2464 // WndProc for the compositor host window. We use this instead of Default so
2465 // we can drop WM_PAINT and WM_ERASEBKGD messages on the floor.
2466 static LRESULT CALLBACK CompositorHostWindowProc(HWND hWnd, UINT message,
2467 WPARAM wParam, LPARAM lParam) {
2472 PaintCompositorHostWindow(hWnd);
2475 return DefWindowProc(hWnd, message, wParam, lParam);
2479 void RenderWidgetHostViewWin::AcceleratedPaint(HDC dc) {
2480 if (render_widget_host_)
2481 render_widget_host_->ScheduleComposite();
2482 if (accelerated_surface_)
2483 accelerated_surface_->Present(dc);
2486 void RenderWidgetHostViewWin::GetScreenInfo(WebKit::WebScreenInfo* results) {
2487 GetScreenInfoForWindow(GetNativeViewId(), results);
2490 gfx::Rect RenderWidgetHostViewWin::GetBoundsInRootWindow() {
2491 RECT window_rect = {0};
2492 HWND root_window = GetAncestor(m_hWnd, GA_ROOT);
2493 ::GetWindowRect(root_window, &window_rect);
2494 gfx::Rect rect(window_rect);
2496 // Maximized windows are outdented from the work area by the frame thickness
2497 // even though this "frame" is not painted. This confuses code (and people)
2498 // that think of a maximized window as corresponding exactly to the work area.
2499 // Correct for this by subtracting the frame thickness back off.
2500 if (::IsZoomed(root_window)) {
2501 rect.Inset(GetSystemMetrics(SM_CXSIZEFRAME),
2502 GetSystemMetrics(SM_CYSIZEFRAME));
2505 return gfx::win::ScreenToDIPRect(rect);
2508 // Creates a HWND within the RenderWidgetHostView that will serve as a host
2509 // for a HWND that the GPU process will create. The host window is used
2510 // to Z-position the GPU's window relative to other plugin windows.
2511 gfx::GLSurfaceHandle RenderWidgetHostViewWin::GetCompositingSurface() {
2512 // If the window has been created, don't recreate it a second time
2513 if (compositor_host_window_)
2514 return gfx::GLSurfaceHandle(compositor_host_window_, gfx::NATIVE_TRANSPORT);
2516 // On Vista and later we present directly to the view window rather than a
2518 if (GpuDataManagerImpl::GetInstance()->IsUsingAcceleratedSurface()) {
2519 if (!accelerated_surface_)
2520 accelerated_surface_.reset(new AcceleratedSurface(m_hWnd));
2521 return gfx::GLSurfaceHandle(m_hWnd, gfx::NATIVE_TRANSPORT);
2524 // On XP we need a child window that can be resized independently of the
2526 static ATOM atom = 0;
2527 static HMODULE instance = NULL;
2529 WNDCLASSEX window_class;
2530 base::win::InitializeWindowClass(
2531 L"CompositorHostWindowClass",
2532 &base::win::WrappedWindowProc<CompositorHostWindowProc>,
2533 0, 0, 0, NULL, NULL, NULL, NULL, NULL,
2535 instance = window_class.hInstance;
2536 atom = RegisterClassEx(&window_class);
2541 GetClientRect(¤tRect);
2543 // Ensure window does not have zero area because D3D cannot create a zero
2545 int width = std::max(1,
2546 static_cast<int>(currentRect.right - currentRect.left));
2547 int height = std::max(1,
2548 static_cast<int>(currentRect.bottom - currentRect.top));
2550 compositor_host_window_ = CreateWindowEx(
2551 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
2552 MAKEINTATOM(atom), 0,
2553 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED,
2554 0, 0, width, height, m_hWnd, 0, instance, 0);
2555 gfx::CheckWindowCreated(compositor_host_window_);
2557 gfx::SetWindowUserData(compositor_host_window_, this);
2559 gfx::GLSurfaceHandle surface_handle(compositor_host_window_,
2560 gfx::NATIVE_TRANSPORT);
2561 return surface_handle;
2564 void RenderWidgetHostViewWin::ResizeCompositingSurface(const gfx::Size& size) {
2565 // Ensure window does not have zero area because D3D cannot create a zero
2567 ::SetWindowPos(compositor_host_window_,
2570 std::max(1, size.width()),
2571 std::max(1, size.height()),
2572 SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOZORDER |
2573 SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOMOVE);
2576 void RenderWidgetHostViewWin::OnAcceleratedCompositingStateChange() {
2577 bool show = render_widget_host_->is_accelerated_compositing_active();
2578 // When we first create the compositor, we will get a show request from
2579 // the renderer before we have gotten the create request from the GPU. In this
2580 // case, simply ignore the show request.
2581 if (compositor_host_window_ == NULL)
2585 ::ShowWindow(compositor_host_window_, SW_SHOW);
2587 // Get all the child windows of this view, including the compositor window.
2588 std::vector<HWND> all_child_windows;
2589 ::EnumChildWindows(m_hWnd, AddChildWindowToVector,
2590 reinterpret_cast<LPARAM>(&all_child_windows));
2592 // Build a list of just the plugin window handles
2593 std::vector<HWND> plugin_windows;
2594 bool compositor_host_window_found = false;
2595 for (size_t i = 0; i < all_child_windows.size(); ++i) {
2596 if (all_child_windows[i] != compositor_host_window_)
2597 plugin_windows.push_back(all_child_windows[i]);
2599 compositor_host_window_found = true;
2601 DCHECK(compositor_host_window_found);
2603 // Set all the plugin windows to be "after" the compositor window.
2604 // When the compositor window is created, gets placed above plugins.
2605 for (size_t i = 0; i < plugin_windows.size(); ++i) {
2607 if (i + 1 < plugin_windows.size())
2608 next = plugin_windows[i+1];
2610 next = compositor_host_window_;
2611 ::SetWindowPos(plugin_windows[i], next, 0, 0, 0, 0,
2612 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
2615 // Drop the backing store for the accelerated surface when the accelerated
2616 // compositor is disabled. Otherwise, a flash of the last presented frame
2617 // could appear when it is next enabled.
2618 if (accelerated_surface_)
2619 accelerated_surface_->Suspend();
2620 hide_compositor_window_at_next_paint_ = true;
2624 void RenderWidgetHostViewWin::AcceleratedSurfaceBuffersSwapped(
2625 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
2630 void RenderWidgetHostViewWin::AcceleratedSurfacePostSubBuffer(
2631 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
2636 void RenderWidgetHostViewWin::AcceleratedSurfaceSuspend() {
2637 if (!accelerated_surface_)
2640 accelerated_surface_->Suspend();
2643 void RenderWidgetHostViewWin::AcceleratedSurfaceRelease() {
2646 bool RenderWidgetHostViewWin::HasAcceleratedSurface(
2647 const gfx::Size& desired_size) {
2648 // TODO(jbates) Implement this so this view can use GetBackingStore for both
2649 // software and GPU frames. Defaulting to false just makes GetBackingStore
2650 // only useable for software frames.
2654 void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) {
2655 if (!render_widget_host_)
2658 render_widget_host_->AccessibilitySetFocus(acc_obj_id);
2661 void RenderWidgetHostViewWin::AccessibilityDoDefaultAction(int acc_obj_id) {
2662 if (!render_widget_host_)
2665 render_widget_host_->AccessibilityDoDefaultAction(acc_obj_id);
2668 void RenderWidgetHostViewWin::AccessibilityScrollToMakeVisible(
2669 int acc_obj_id, gfx::Rect subfocus) {
2670 if (!render_widget_host_)
2673 render_widget_host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
2676 void RenderWidgetHostViewWin::AccessibilityScrollToPoint(
2677 int acc_obj_id, gfx::Point point) {
2678 if (!render_widget_host_)
2681 render_widget_host_->AccessibilityScrollToPoint(acc_obj_id, point);
2684 void RenderWidgetHostViewWin::AccessibilitySetTextSelection(
2685 int acc_obj_id, int start_offset, int end_offset) {
2686 if (!render_widget_host_)
2689 render_widget_host_->AccessibilitySetTextSelection(
2690 acc_obj_id, start_offset, end_offset);
2693 gfx::Point RenderWidgetHostViewWin::GetLastTouchEventLocation() const {
2694 return last_touch_location_;
2697 void RenderWidgetHostViewWin::FatalAccessibilityTreeError() {
2698 render_widget_host_->FatalAccessibilityTreeError();
2699 SetBrowserAccessibilityManager(NULL);
2702 LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam,
2703 LPARAM lparam, BOOL& handled) {
2704 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGetObject");
2705 if (kIdCustom == lparam) {
2706 // An MSAA client requestes our custom id. Assume that we have detected an
2707 // active windows screen reader.
2708 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
2709 render_widget_host_->SetAccessibilityMode(
2710 BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode());
2712 // Return with failure.
2713 return static_cast<LRESULT>(0L);
2716 if (lparam != OBJID_CLIENT) {
2718 return static_cast<LRESULT>(0L);
2721 IAccessible* iaccessible = GetNativeViewAccessible();
2723 return LresultFromObject(IID_IAccessible, wparam, iaccessible);
2726 return static_cast<LRESULT>(0L);
2729 LRESULT RenderWidgetHostViewWin::OnParentNotify(UINT message, WPARAM wparam,
2730 LPARAM lparam, BOOL& handled) {
2731 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnParentNotify");
2734 if (!render_widget_host_)
2737 switch (LOWORD(wparam)) {
2738 case WM_LBUTTONDOWN:
2739 case WM_RBUTTONDOWN:
2740 case WM_MBUTTONDOWN:
2741 render_widget_host_->StartUserGesture();
2749 void RenderWidgetHostViewWin::OnFinalMessage(HWND window) {
2750 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnFinalMessage");
2751 // When the render widget host is being destroyed, it ends up calling
2752 // Destroy() which NULLs render_widget_host_.
2753 // Note: the following bug http://crbug.com/24248 seems to report that
2754 // OnFinalMessage is called with a deleted |render_widget_host_|. It is not
2755 // clear how this could happen, hence the NULLing of render_widget_host_
2757 if (!render_widget_host_ && !being_destroyed_) {
2758 // If you hit this NOTREACHED, please add a comment to report it on
2759 // http://crbug.com/24248, including what you did when it happened and if
2763 if (render_widget_host_)
2764 render_widget_host_->ViewDestroyed();
2765 if (base::win::IsTSFAwareRequired())
2766 ui::TSFBridge::GetInstance()->RemoveFocusedClient(this);
2770 LRESULT RenderWidgetHostViewWin::OnSessionChange(UINT message,
2775 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSessionChange");
2777 if (!accelerated_surface_)
2781 case WTS_SESSION_LOCK:
2782 accelerated_surface_->SetIsSessionLocked(true);
2784 case WTS_SESSION_UNLOCK:
2785 // Force a repaint to update the window contents.
2786 if (!render_widget_host_->is_hidden())
2787 InvalidateRect(NULL, FALSE);
2788 accelerated_surface_->SetIsSessionLocked(false);
2797 void RenderWidgetHostViewWin::TrackMouseLeave(bool track) {
2798 if (track == track_mouse_leave_)
2800 track_mouse_leave_ = track;
2804 TRACKMOUSEEVENT tme;
2805 tme.cbSize = sizeof(TRACKMOUSEEVENT);
2806 tme.dwFlags = TME_LEAVE;
2807 if (!track_mouse_leave_)
2808 tme.dwFlags |= TME_CANCEL;
2809 tme.hwndTrack = m_hWnd;
2811 TrackMouseEvent(&tme);
2814 bool RenderWidgetHostViewWin::Send(IPC::Message* message) {
2815 if (!render_widget_host_)
2817 return render_widget_host_->Send(message);
2820 void RenderWidgetHostViewWin::EnsureTooltip() {
2821 UINT message = TTM_NEWTOOLRECT;
2824 ti.cbSize = sizeof(ti);
2827 if (!::IsWindow(tooltip_hwnd_)) {
2828 message = TTM_ADDTOOL;
2829 tooltip_hwnd_ = CreateWindowEx(
2830 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
2831 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, m_hWnd, NULL,
2833 if (!tooltip_hwnd_) {
2834 // Tooltip creation can inexplicably fail. See bug 82913 for details.
2835 LOG_GETLASTERROR(WARNING) <<
2836 "Tooltip creation failed, tooltips won't work";
2839 ti.uFlags = TTF_TRANSPARENT;
2840 ti.lpszText = LPSTR_TEXTCALLBACK;
2842 // Ensure web content tooltips are displayed for at least this amount of
2843 // time, to give users a chance to read longer messages.
2844 const int kMinimumAutopopDurationMs = 10 * 1000;
2845 int autopop_duration_ms =
2846 SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_AUTOPOP, NULL);
2847 if (autopop_duration_ms < kMinimumAutopopDurationMs) {
2848 SendMessage(tooltip_hwnd_, TTM_SETDELAYTIME, TTDT_AUTOPOP,
2849 kMinimumAutopopDurationMs);
2854 GetClientRect(&ti.rect);
2855 SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti));
2858 void RenderWidgetHostViewWin::ResetTooltip() {
2859 if (::IsWindow(tooltip_hwnd_))
2860 ::DestroyWindow(tooltip_hwnd_);
2861 tooltip_hwnd_ = NULL;
2864 bool RenderWidgetHostViewWin::ForwardGestureEventToRenderer(
2865 ui::GestureEvent* gesture) {
2866 if (!render_widget_host_)
2869 // Pinch gestures are disabled by default on windows desktop. See
2870 // crbug.com/128477 and crbug.com/148816
2871 if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN ||
2872 gesture->type() == ui::ET_GESTURE_PINCH_UPDATE ||
2873 gesture->type() == ui::ET_GESTURE_PINCH_END) &&
2874 !ShouldSendPinchGesture()) {
2878 WebKit::WebGestureEvent web_gesture = CreateWebGestureEvent(m_hWnd, *gesture);
2879 if (web_gesture.type == WebKit::WebGestureEvent::Undefined)
2881 if (web_gesture.type == WebKit::WebGestureEvent::GestureTapDown) {
2882 render_widget_host_->ForwardGestureEvent(
2883 CreateFlingCancelEvent(gesture->time_stamp().InSecondsF()));
2885 render_widget_host_->ForwardGestureEventWithLatencyInfo(web_gesture,
2886 *gesture->latency());
2890 void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message,
2893 TRACE_EVENT0("browser",
2894 "RenderWidgetHostViewWin::ForwardMouseEventToRenderer");
2895 if (!render_widget_host_) {
2896 TRACE_EVENT0("browser", "EarlyOut_NoRWH");
2900 gfx::Point point = gfx::win::ScreenToDIPPoint(
2901 gfx::Point(static_cast<short>(LOWORD(lparam)),
2902 static_cast<short>(HIWORD(lparam))));
2903 lparam = MAKELPARAM(point.x(), point.y());
2905 WebMouseEvent event(
2906 WebMouseEventBuilder::Build(m_hWnd, message, wparam, lparam));
2908 if (mouse_locked_) {
2909 event.movementX = event.globalX - last_mouse_position_.locked_global.x();
2910 event.movementY = event.globalY - last_mouse_position_.locked_global.y();
2911 last_mouse_position_.locked_global.SetPoint(event.globalX, event.globalY);
2913 event.x = last_mouse_position_.unlocked.x();
2914 event.y = last_mouse_position_.unlocked.y();
2915 event.windowX = last_mouse_position_.unlocked.x();
2916 event.windowY = last_mouse_position_.unlocked.y();
2917 event.globalX = last_mouse_position_.unlocked_global.x();
2918 event.globalY = last_mouse_position_.unlocked_global.y();
2920 if (ignore_mouse_movement_) {
2921 ignore_mouse_movement_ = false;
2922 event.movementX = 0;
2923 event.movementY = 0;
2926 event.globalX - last_mouse_position_.unlocked_global.x();
2928 event.globalY - last_mouse_position_.unlocked_global.y();
2931 last_mouse_position_.unlocked.SetPoint(event.windowX, event.windowY);
2932 last_mouse_position_.unlocked_global.SetPoint(event.globalX, event.globalY);
2935 // Windows sends (fake) mouse messages for touch events. Don't send these to
2936 // the render widget.
2937 if (!touch_events_enabled_ || !ui::IsMouseEventFromTouch(message)) {
2938 // Send the event to the renderer before changing mouse capture, so that
2939 // the capturelost event arrives after mouseup.
2940 render_widget_host_->ForwardMouseEvent(event);
2942 switch (event.type) {
2943 case WebInputEvent::MouseMove:
2944 TrackMouseLeave(true);
2946 case WebInputEvent::MouseLeave:
2947 TrackMouseLeave(false);
2949 case WebInputEvent::MouseDown:
2952 case WebInputEvent::MouseUp:
2953 if (GetCapture() == m_hWnd)
2959 if (IsActivatable() && event.type == WebInputEvent::MouseDown) {
2960 // This is a temporary workaround for bug 765011 to get focus when the
2961 // mouse is clicked. This happens after the mouse down event is sent to
2962 // the renderer because normally Windows does a WM_SETFOCUS after
2968 void RenderWidgetHostViewWin::ShutdownHost() {
2969 weak_factory_.InvalidateWeakPtrs();
2970 if (render_widget_host_)
2971 render_widget_host_->Shutdown();
2972 // Do not touch any members at this point, |this| has been deleted.
2975 void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd,
2976 const gfx::Rect& pos,
2978 Create(parent_hwnd, NULL, NULL, WS_POPUP, ex_style);
2979 gfx::Rect screen_rect = gfx::win::DIPToScreenRect(pos);
2980 MoveWindow(screen_rect.x(), screen_rect.y(), screen_rect.width(),
2981 screen_rect.height(), TRUE);
2982 ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA);
2984 if (is_fullscreen_ && win8::IsSingleWindowMetroMode()) {
2985 MetroSetFrameWindow set_frame_window =
2986 reinterpret_cast<MetroSetFrameWindow>(
2987 ::GetProcAddress(base::win::GetMetroModule(), "SetFrameWindow"));
2988 DCHECK(set_frame_window);
2989 set_frame_window(m_hWnd);
2993 CPoint RenderWidgetHostViewWin::GetClientCenter() const {
2995 GetClientRect(&rect);
2996 return rect.CenterPoint();
2999 void RenderWidgetHostViewWin::MoveCursorToCenterIfNecessary() {
3000 DCHECK(mouse_locked_);
3003 GetClipCursor(&rect);
3004 int border_x = rect.Width() * kMouseLockBorderPercentage / 100;
3005 int border_y = rect.Height() * kMouseLockBorderPercentage / 100;
3008 last_mouse_position_.locked_global.x() < rect.left + border_x ||
3009 last_mouse_position_.locked_global.x() > rect.right - border_x ||
3010 last_mouse_position_.locked_global.y() < rect.top + border_y ||
3011 last_mouse_position_.locked_global.y() > rect.bottom - border_y;
3014 move_to_center_request_.pending = true;
3015 move_to_center_request_.target = rect.CenterPoint();
3016 if (!::SetCursorPos(move_to_center_request_.target.x(),
3017 move_to_center_request_.target.y())) {
3018 LOG_GETLASTERROR(WARNING) << "Failed to set cursor position.";
3023 void RenderWidgetHostViewWin::HandleLockedMouseEvent(UINT message,
3026 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::HandleLockedMouseEvent");
3027 DCHECK(mouse_locked_);
3029 if (message == WM_MOUSEMOVE && move_to_center_request_.pending) {
3030 // Ignore WM_MOUSEMOVE messages generated by
3031 // MoveCursorToCenterIfNecessary().
3032 CPoint current_position(LOWORD(lparam), HIWORD(lparam));
3033 ClientToScreen(¤t_position);
3034 if (move_to_center_request_.target.x() == current_position.x &&
3035 move_to_center_request_.target.y() == current_position.y) {
3036 move_to_center_request_.pending = false;
3037 last_mouse_position_.locked_global = move_to_center_request_.target;
3042 ForwardMouseEventToRenderer(message, wparam, lparam);
3045 LRESULT RenderWidgetHostViewWin::OnDocumentFeed(RECONVERTSTRING* reconv) {
3046 size_t target_offset;
3047 size_t target_length;
3048 bool has_composition;
3049 if (!composition_range_.is_empty()) {
3050 target_offset = composition_range_.GetMin();
3051 target_length = composition_range_.length();
3052 has_composition = true;
3053 } else if (selection_range_.IsValid()) {
3054 target_offset = selection_range_.GetMin();
3055 target_length = selection_range_.length();
3056 has_composition = false;
3061 size_t len = selection_text_.length();
3062 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
3064 if (target_offset < selection_text_offset_ ||
3065 target_offset + target_length > selection_text_offset_ + len) {
3072 if (reconv->dwSize < need_size)
3075 reconv->dwVersion = 0;
3076 reconv->dwStrLen = len;
3077 reconv->dwStrOffset = sizeof(RECONVERTSTRING);
3078 reconv->dwCompStrLen = has_composition ? target_length: 0;
3079 reconv->dwCompStrOffset =
3080 (target_offset - selection_text_offset_) * sizeof(WCHAR);
3081 reconv->dwTargetStrLen = target_length;
3082 reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
3083 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING),
3084 selection_text_.c_str(), len * sizeof(WCHAR));
3086 // According to Microsft API document, IMR_RECONVERTSTRING and
3087 // IMR_DOCUMENTFEED should return reconv, but some applications return
3089 return reinterpret_cast<LRESULT>(reconv);
3092 LRESULT RenderWidgetHostViewWin::OnReconvertString(RECONVERTSTRING* reconv) {
3093 // If there is a composition string already, we don't allow reconversion.
3094 if (imm32_manager_->is_composing())
3097 if (selection_range_.is_empty())
3100 if (selection_text_.empty())
3103 if (selection_range_.GetMin() < selection_text_offset_ ||
3104 selection_range_.GetMax() >
3105 selection_text_offset_ + selection_text_.length()) {
3109 size_t len = selection_range_.length();
3110 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
3115 if (reconv->dwSize < need_size)
3118 reconv->dwVersion = 0;
3119 reconv->dwStrLen = len;
3120 reconv->dwStrOffset = sizeof(RECONVERTSTRING);
3121 reconv->dwCompStrLen = len;
3122 reconv->dwCompStrOffset = 0;
3123 reconv->dwTargetStrLen = len;
3124 reconv->dwTargetStrOffset = 0;
3126 size_t offset = selection_range_.GetMin() - selection_text_offset_;
3127 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING),
3128 selection_text_.c_str() + offset, len * sizeof(WCHAR));
3130 // According to Microsft API document, IMR_RECONVERTSTRING and
3131 // IMR_DOCUMENTFEED should return reconv, but some applications return
3133 return reinterpret_cast<LRESULT>(reconv);
3136 LRESULT RenderWidgetHostViewWin::OnQueryCharPosition(
3137 IMECHARPOSITION* position) {
3140 if (position->dwSize < sizeof(IMECHARPOSITION))
3143 RECT target_rect = {};
3144 if (imm32_manager_->is_composing() && !composition_range_.is_empty() &&
3145 position->dwCharPos < composition_character_bounds_.size()) {
3147 composition_character_bounds_[position->dwCharPos].ToRECT();
3148 } else if (position->dwCharPos == 0) {
3149 // When there is no on-going composition but |position->dwCharPos| is 0,
3150 // use the caret rect. This behavior is the same to RichEdit. In fact,
3151 // CUAS (Cicero Unaware Application Support) relies on this behavior to
3152 // implement ITfContextView::GetTextExt on top of IMM32-based applications.
3153 target_rect = caret_rect_.ToRECT();
3157 ClientToScreen(&target_rect);
3159 RECT document_rect = GetPixelBounds().ToRECT();
3160 ClientToScreen(&document_rect);
3162 position->pt.x = target_rect.left;
3163 position->pt.y = target_rect.top;
3164 position->cLineHeight = target_rect.bottom - target_rect.top;
3165 position->rcDocument = document_rect;
3169 void RenderWidgetHostViewWin::UpdateIMEState() {
3170 if (base::win::IsTSFAwareRequired()) {
3171 ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(this);
3174 if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
3175 text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) {
3176 imm32_manager_->EnableIME(m_hWnd);
3177 imm32_manager_->SetUseCompositionWindow(!can_compose_inline_);
3179 imm32_manager_->DisableIME(m_hWnd);
3182 imm32_manager_->SetTextInputMode(m_hWnd, text_input_mode_);
3185 void RenderWidgetHostViewWin::UpdateInputScopeIfNecessary(
3186 ui::TextInputType text_input_type) {
3187 // The text store is responsible for handling input scope when TSF-aware is
3189 if (base::win::IsTSFAwareRequired())
3192 ui::tsf_inputscope::SetInputScopeForTsfUnawareWindow(
3193 m_hWnd, text_input_type, text_input_mode_);
3196 ////////////////////////////////////////////////////////////////////////////////
3197 // RenderWidgetHostView, public:
3200 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
3201 RenderWidgetHost* widget) {
3202 return new RenderWidgetHostViewWin(widget);
3206 void RenderWidgetHostViewPort::GetDefaultScreenInfo(
3207 WebKit::WebScreenInfo* results) {
3208 GetScreenInfoForWindow(0, results);
3211 } // namespace content