OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
scottmg
2014/01/14 17:28:33
BOOM
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/renderer_host/render_widget_host_view_win.h" | |
6 | |
7 #include <InputScope.h> | |
8 #include <wtsapi32.h> | |
9 #pragma comment(lib, "wtsapi32.lib") | |
10 | |
11 #include <algorithm> | |
12 #include <map> | |
13 #include <stack> | |
14 | |
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" | |
82 | |
83 using base::TimeDelta; | |
84 using base::TimeTicks; | |
85 using ui::ViewProp; | |
86 using blink::WebInputEvent; | |
87 using blink::WebMouseEvent; | |
88 using blink::WebTextDirection; | |
89 | |
90 namespace content { | |
91 namespace { | |
92 | |
93 // Tooltips will wrap after this width. Yes, wrap. Imagine that! | |
94 const int kTooltipMaxWidthPixels = 300; | |
95 | |
96 // Maximum number of characters we allow in a tooltip. | |
97 const int kMaxTooltipLength = 1024; | |
98 | |
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; | |
102 | |
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; | |
106 | |
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; | |
115 | |
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); | |
120 | |
121 if (::IsWindowVisible(window)) { | |
122 const HWND owner = ::GetWindow(window, GW_OWNER); | |
123 if (toplevel_hwnd == owner) { | |
124 ::PostMessage(window, WM_CANCELMODE, 0, 0); | |
125 } | |
126 } | |
127 | |
128 return TRUE; | |
129 } | |
130 | |
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) | |
134 return; | |
135 | |
136 gpu_process_host->Send(message.release()); | |
137 } | |
138 | |
139 void PostTaskOnIOThread(const tracked_objects::Location& from_here, | |
140 base::Closure task) { | |
141 BrowserThread::PostTask(BrowserThread::IO, from_here, task); | |
142 } | |
143 | |
144 bool DecodeZoomGesture(HWND hwnd, const GESTUREINFO& gi, | |
145 PageZoom* zoom, POINT* zoom_center) { | |
146 static long start = 0; | |
147 static POINT zoom_first; | |
148 | |
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); | |
154 return false; | |
155 } | |
156 | |
157 if (gi.dwFlags == GF_END) | |
158 return false; | |
159 | |
160 POINT zoom_second = {0}; | |
161 zoom_second.x = gi.ptsLocation.x; | |
162 zoom_second.y = gi.ptsLocation.y; | |
163 ScreenToClient(hwnd, &zoom_second); | |
164 | |
165 if (zoom_first.x == zoom_second.x && zoom_first.y == zoom_second.y) | |
166 return false; | |
167 | |
168 zoom_center->x = (zoom_first.x + zoom_second.x) / 2; | |
169 zoom_center->y = (zoom_first.y + zoom_second.y) / 2; | |
170 | |
171 double zoom_factor = | |
172 static_cast<double>(gi.ullArguments)/static_cast<double>(start); | |
173 | |
174 *zoom = zoom_factor >= 1 ? PAGE_ZOOM_IN : PAGE_ZOOM_OUT; | |
175 | |
176 start = gi.ullArguments; | |
177 zoom_first = zoom_second; | |
178 return true; | |
179 } | |
180 | |
181 bool DecodeScrollGesture(const GESTUREINFO& gi, | |
182 POINT* start, | |
183 POINT* delta){ | |
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; | |
189 | |
190 if (gi.dwFlags == GF_BEGIN) { | |
191 delta->x = 0; | |
192 delta->y = 0; | |
193 start_pt.x = gi.ptsLocation.x; | |
194 start_pt.y = gi.ptsLocation.y; | |
195 } else { | |
196 delta->x = gi.ptsLocation.x - last_pt.x; | |
197 delta->y = gi.ptsLocation.y - last_pt.y; | |
198 } | |
199 last_pt.x = gi.ptsLocation.x; | |
200 last_pt.y = gi.ptsLocation.y; | |
201 *start = start_pt; | |
202 return true; | |
203 } | |
204 | |
205 blink::WebMouseWheelEvent MakeFakeScrollWheelEvent(HWND hwnd, | |
206 POINT start, | |
207 POINT delta) { | |
208 blink::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; | |
226 return result; | |
227 } | |
228 | |
229 static const int kTouchMask = 0x7; | |
230 | |
231 inline int GetTouchType(const TOUCHINPUT& point) { | |
232 return point.dwFlags & kTouchMask; | |
233 } | |
234 | |
235 inline void SetTouchType(TOUCHINPUT* point, int type) { | |
236 point->dwFlags = (point->dwFlags & kTouchMask) | type; | |
237 } | |
238 | |
239 ui::EventType ConvertToUIEvent(blink::WebTouchPoint::State t) { | |
240 switch (t) { | |
241 case blink::WebTouchPoint::StatePressed: | |
242 return ui::ET_TOUCH_PRESSED; | |
243 case blink::WebTouchPoint::StateMoved: | |
244 return ui::ET_TOUCH_MOVED; | |
245 case blink::WebTouchPoint::StateStationary: | |
246 return ui::ET_TOUCH_STATIONARY; | |
247 case blink::WebTouchPoint::StateReleased: | |
248 return ui::ET_TOUCH_RELEASED; | |
249 case blink::WebTouchPoint::StateCancelled: | |
250 return ui::ET_TOUCH_CANCELLED; | |
251 default: | |
252 DCHECK(false) << "Unexpected ui type. " << t; | |
253 return ui::ET_UNKNOWN; | |
254 } | |
255 } | |
256 | |
257 // Creates a WebGestureEvent corresponding to the given |gesture| | |
258 blink::WebGestureEvent CreateWebGestureEvent(HWND hwnd, | |
259 const ui::GestureEvent& gesture) { | |
260 blink::WebGestureEvent gesture_event = | |
261 MakeWebGestureEventFromUIEvent(gesture); | |
262 | |
263 POINT client_point = gesture.location().ToPOINT(); | |
264 POINT screen_point = gesture.location().ToPOINT(); | |
265 MapWindowPoints(hwnd, HWND_DESKTOP, &screen_point, 1); | |
266 | |
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; | |
271 | |
272 return gesture_event; | |
273 } | |
274 | |
275 blink::WebGestureEvent CreateFlingCancelEvent(double time_stamp) { | |
276 blink::WebGestureEvent gesture_event; | |
277 gesture_event.timeStampSeconds = time_stamp; | |
278 gesture_event.type = blink::WebGestureEvent::GestureFlingCancel; | |
279 gesture_event.sourceDevice = blink::WebGestureEvent::Touchscreen; | |
280 return gesture_event; | |
281 } | |
282 | |
283 class TouchEventFromWebTouchPoint : public ui::TouchEvent { | |
284 public: | |
285 TouchEventFromWebTouchPoint(const blink::WebTouchPoint& touch_point, | |
286 base::TimeDelta& timestamp) | |
287 : ui::TouchEvent(ConvertToUIEvent(touch_point.state), | |
288 touch_point.position, | |
289 touch_point.id, | |
290 timestamp) { | |
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()); | |
295 } | |
296 | |
297 virtual ~TouchEventFromWebTouchPoint() {} | |
298 | |
299 private: | |
300 DISALLOW_COPY_AND_ASSIGN(TouchEventFromWebTouchPoint); | |
301 }; | |
302 | |
303 bool ShouldSendPinchGesture() { | |
304 static bool pinch_allowed = | |
305 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch); | |
306 return pinch_allowed; | |
307 } | |
308 | |
309 void GetScreenInfoForWindow(gfx::NativeViewId id, | |
310 blink::WebScreenInfo* results) { | |
311 HWND window = gfx::NativeViewFromId(id); | |
312 | |
313 HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); | |
314 | |
315 MONITORINFOEX monitor_info; | |
316 monitor_info.cbSize = sizeof(MONITORINFOEX); | |
317 if (!base::win::GetMonitorInfoWrapper(monitor, &monitor_info)) | |
318 return; | |
319 | |
320 DEVMODE dev_mode; | |
321 dev_mode.dmSize = sizeof(dev_mode); | |
322 dev_mode.dmDriverExtra = 0; | |
323 EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode); | |
324 | |
325 blink::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); | |
332 | |
333 *results = screen_info; | |
334 } | |
335 | |
336 } // namespace | |
337 | |
338 const wchar_t kRenderWidgetHostHWNDClass[] = L"Chrome_RenderWidgetHostHWND"; | |
339 | |
340 // Wrapper for maintaining touchstate associated with a WebTouchEvent. | |
341 class WebTouchState { | |
342 public: | |
343 explicit WebTouchState(const RenderWidgetHostViewWin* window); | |
344 | |
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); | |
349 | |
350 // Marks all active touchpoints as released. | |
351 bool ReleaseTouchPoints(); | |
352 | |
353 // The contained WebTouchEvent. | |
354 const blink::WebTouchEvent& touch_event() { return touch_event_; } | |
355 | |
356 // Returns if any touches are modified in the event. | |
357 bool is_changed() { return touch_event_.changedTouchesLength != 0; } | |
358 | |
359 private: | |
360 // Adds a touch point or returns NULL if there's not enough space. | |
361 blink::WebTouchPoint* AddTouchPoint(TOUCHINPUT* touch_input); | |
362 | |
363 // Copy details from a TOUCHINPUT to an existing WebTouchPoint, returning | |
364 // true if the resulting point is a stationary move. | |
365 bool UpdateTouchPoint(blink::WebTouchPoint* touch_point, | |
366 TOUCHINPUT* touch_input); | |
367 | |
368 // Find (or create) a mapping for _os_touch_id_. | |
369 unsigned int GetMappedTouch(unsigned int os_touch_id); | |
370 | |
371 // Remove any mappings that are no longer in use. | |
372 void RemoveExpiredMappings(); | |
373 | |
374 blink::WebTouchEvent touch_event_; | |
375 const RenderWidgetHostViewWin* const window_; | |
376 | |
377 ui::SequentialIDGenerator id_generator_; | |
378 | |
379 DISALLOW_COPY_AND_ASSIGN(WebTouchState); | |
380 }; | |
381 | |
382 typedef void (*MetroSetFrameWindow)(HWND window); | |
383 typedef void (*MetroCloseFrameWindow)(HWND window); | |
384 | |
385 /////////////////////////////////////////////////////////////////////////////// | |
386 // RenderWidgetHostViewWin, public: | |
387 | |
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), | |
399 tooltip_hwnd_(NULL), | |
400 tooltip_showing_(false), | |
401 weak_factory_(this), | |
402 is_loading_(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); | |
415 registrar_.Add(this, | |
416 NOTIFICATION_RENDERER_PROCESS_TERMINATED, | |
417 NotificationService::AllBrowserContextsAndSources()); | |
418 gesture_recognizer_->AddGestureEventHelper(this); | |
419 } | |
420 | |
421 RenderWidgetHostViewWin::~RenderWidgetHostViewWin() { | |
422 gesture_recognizer_->RemoveGestureEventHelper(this); | |
423 UnlockMouse(); | |
424 ResetTooltip(); | |
425 } | |
426 | |
427 void RenderWidgetHostViewWin::CreateWnd(HWND parent) { | |
428 // ATL function to create the window. | |
429 Create(parent); | |
430 } | |
431 | |
432 /////////////////////////////////////////////////////////////////////////////// | |
433 // RenderWidgetHostViewWin, RenderWidgetHostView implementation: | |
434 | |
435 void RenderWidgetHostViewWin::InitAsChild( | |
436 gfx::NativeView parent_view) { | |
437 CreateWnd(parent_view); | |
438 } | |
439 | |
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, | |
444 WS_EX_TOOLWINDOW); | |
445 } | |
446 | |
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); | |
453 } | |
454 | |
455 RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const { | |
456 return render_widget_host_; | |
457 } | |
458 | |
459 void RenderWidgetHostViewWin::WasShown() { | |
460 // |render_widget_host_| may be NULL if the WebContentsImpl is in the process | |
461 // of closing. | |
462 if (!render_widget_host_) | |
463 return; | |
464 | |
465 if (!render_widget_host_->is_hidden()) | |
466 return; | |
467 | |
468 if (web_contents_switch_paint_time_.is_null()) | |
469 web_contents_switch_paint_time_ = TimeTicks::Now(); | |
470 | |
471 render_widget_host_->WasShown(); | |
472 } | |
473 | |
474 void RenderWidgetHostViewWin::WasHidden() { | |
475 // |render_widget_host_| may be NULL if the WebContentsImpl is in the process | |
476 // of closing. | |
477 if (!render_widget_host_) | |
478 return; | |
479 | |
480 if (render_widget_host_->is_hidden()) | |
481 return; | |
482 | |
483 ResetTooltip(); | |
484 | |
485 // Inform the renderer that we are being hidden so it can reduce its resource | |
486 // utilization. | |
487 render_widget_host_->WasHidden(); | |
488 | |
489 if (accelerated_surface_) | |
490 accelerated_surface_->WasHidden(); | |
491 | |
492 if (GetBrowserAccessibilityManager()) | |
493 GetBrowserAccessibilityManager()->WasHidden(); | |
494 | |
495 web_contents_switch_paint_time_ = base::TimeTicks(); | |
496 } | |
497 | |
498 void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) { | |
499 SetBounds(gfx::Rect(GetPixelBounds().origin(), size)); | |
500 } | |
501 | |
502 void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) { | |
503 if (being_destroyed_) | |
504 return; | |
505 | |
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; | |
510 | |
511 // If the style is not popup, you have to convert the point to client | |
512 // coordinate. | |
513 POINT point = { rect.x(), rect.y() }; | |
514 if (GetStyle() & WS_CHILD) | |
515 ScreenToClient(&point); | |
516 | |
517 SetWindowPos(NULL, point.x, point.y, rect.width(), rect.height(), swp_flags); | |
518 render_widget_host_->WasResized(); | |
519 } | |
520 | |
521 gfx::NativeView RenderWidgetHostViewWin::GetNativeView() const { | |
522 return m_hWnd; | |
523 } | |
524 | |
525 gfx::NativeViewId RenderWidgetHostViewWin::GetNativeViewId() const { | |
526 return reinterpret_cast<gfx::NativeViewId>(m_hWnd); | |
527 } | |
528 | |
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); | |
535 } | |
536 | |
537 CreateBrowserAccessibilityManagerIfNeeded(); | |
538 | |
539 return GetBrowserAccessibilityManager()->GetRoot()-> | |
540 ToBrowserAccessibilityWin(); | |
541 } | |
542 | |
543 void RenderWidgetHostViewWin::MovePluginWindows( | |
544 const gfx::Vector2d& scroll_offset, | |
545 const std::vector<WebPluginGeometry>& plugin_window_moves) { | |
546 MovePluginWindowsHelper(m_hWnd, plugin_window_moves); | |
547 } | |
548 | |
549 static BOOL CALLBACK AddChildWindowToVector(HWND hwnd, LPARAM lparam) { | |
550 std::vector<HWND>* vector = reinterpret_cast<std::vector<HWND>*>(lparam); | |
551 vector->push_back(hwnd); | |
552 return TRUE; | |
553 } | |
554 | |
555 void RenderWidgetHostViewWin::CleanupCompositorWindow() { | |
556 if (!compositor_host_window_) | |
557 return; | |
558 | |
559 gfx::SetWindowUserData(compositor_host_window_, NULL); | |
560 | |
561 // Hide the compositor and parent it to the desktop rather than destroying | |
562 // it immediately. The GPU process has a grace period to stop accessing the | |
563 // window. TODO(apatrick): the GPU process should acknowledge that it has | |
564 // finished with the window handle and the browser process should destroy it | |
565 // at that point. | |
566 ::ShowWindow(compositor_host_window_, SW_HIDE); | |
567 ::SetParent(compositor_host_window_, NULL); | |
568 | |
569 BrowserThread::PostDelayedTask( | |
570 BrowserThread::UI, | |
571 FROM_HERE, | |
572 base::Bind(base::IgnoreResult(&::DestroyWindow), | |
573 compositor_host_window_), | |
574 base::TimeDelta::FromMilliseconds(kDestroyCompositorHostWindowDelay)); | |
575 | |
576 compositor_host_window_ = NULL; | |
577 } | |
578 | |
579 bool RenderWidgetHostViewWin::IsActivatable() const { | |
580 // Popups should not be activated. | |
581 return popup_type_ == blink::WebPopupTypeNone; | |
582 } | |
583 | |
584 void RenderWidgetHostViewWin::Focus() { | |
585 if (IsWindow()) | |
586 SetFocus(); | |
587 } | |
588 | |
589 void RenderWidgetHostViewWin::Blur() { | |
590 NOTREACHED(); | |
591 } | |
592 | |
593 bool RenderWidgetHostViewWin::HasFocus() const { | |
594 return ::GetFocus() == m_hWnd; | |
595 } | |
596 | |
597 bool RenderWidgetHostViewWin::IsSurfaceAvailableForCopy() const { | |
598 if (render_widget_host_->is_accelerated_compositing_active()) | |
599 return accelerated_surface_.get() && accelerated_surface_->IsReadyForCopy(); | |
600 else | |
601 return !!render_widget_host_->GetBackingStore(false); | |
602 } | |
603 | |
604 void RenderWidgetHostViewWin::Show() { | |
605 ShowWindow(SW_SHOW); | |
606 WasShown(); | |
607 } | |
608 | |
609 void RenderWidgetHostViewWin::Hide() { | |
610 if (!is_fullscreen_ && GetParent() == gfx::GetWindowToParentTo(true)) { | |
611 LOG(WARNING) << "Hide() called twice in a row: " << this << ":" | |
612 << GetParent(); | |
613 return; | |
614 } | |
615 | |
616 if (::GetFocus() == m_hWnd) | |
617 ::SetFocus(NULL); | |
618 ShowWindow(SW_HIDE); | |
619 | |
620 WasHidden(); | |
621 } | |
622 | |
623 bool RenderWidgetHostViewWin::IsShowing() { | |
624 return !!IsWindowVisible(); | |
625 } | |
626 | |
627 gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const { | |
628 return gfx::win::ScreenToDIPRect(GetPixelBounds()); | |
629 } | |
630 | |
631 gfx::Rect RenderWidgetHostViewWin::GetPixelBounds() const { | |
632 CRect window_rect; | |
633 GetWindowRect(&window_rect); | |
634 return gfx::Rect(window_rect); | |
635 } | |
636 | |
637 void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) { | |
638 current_cursor_ = cursor; | |
639 UpdateCursorIfOverSelf(); | |
640 } | |
641 | |
642 void RenderWidgetHostViewWin::UpdateCursorIfOverSelf() { | |
643 static HCURSOR kCursorArrow = LoadCursor(NULL, IDC_ARROW); | |
644 static HCURSOR kCursorAppStarting = LoadCursor(NULL, IDC_APPSTARTING); | |
645 static HINSTANCE module_handle = GetModuleHandle( | |
646 GetContentClient()->browser()->GetResourceDllName()); | |
647 | |
648 // If the mouse is over our HWND, then update the cursor state immediately. | |
649 CPoint pt; | |
650 GetCursorPos(&pt); | |
651 if (WindowFromPoint(pt) == m_hWnd) { | |
652 // We cannot pass in NULL as the module handle as this would only work for | |
653 // standard win32 cursors. We can also receive cursor types which are | |
654 // defined as webkit resources. We need to specify the module handle of | |
655 // chrome.dll while loading these cursors. | |
656 HCURSOR display_cursor = current_cursor_.GetCursor(module_handle); | |
657 | |
658 // If a page is in the loading state, we want to show the Arrow+Hourglass | |
659 // cursor only when the current cursor is the ARROW cursor. In all other | |
660 // cases we should continue to display the current cursor. | |
661 if (is_loading_ && display_cursor == kCursorArrow) | |
662 display_cursor = kCursorAppStarting; | |
663 | |
664 SetCursor(display_cursor); | |
665 } | |
666 } | |
667 | |
668 void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) { | |
669 is_loading_ = is_loading; | |
670 UpdateCursorIfOverSelf(); | |
671 } | |
672 | |
673 void RenderWidgetHostViewWin::TextInputTypeChanged( | |
674 ui::TextInputType type, | |
675 ui::TextInputMode input_mode, | |
676 bool can_compose_inline) { | |
677 if (text_input_type_ != type || | |
678 text_input_mode_ != input_mode || | |
679 can_compose_inline_ != can_compose_inline) { | |
680 const bool text_input_type_changed = (text_input_type_ != type) || | |
681 (text_input_mode_ != input_mode); | |
682 text_input_type_ = type; | |
683 text_input_mode_ = input_mode; | |
684 can_compose_inline_ = can_compose_inline; | |
685 UpdateIMEState(); | |
686 if (text_input_type_changed) | |
687 UpdateInputScopeIfNecessary(text_input_type_); | |
688 } | |
689 } | |
690 | |
691 void RenderWidgetHostViewWin::SelectionBoundsChanged( | |
692 const ViewHostMsg_SelectionBounds_Params& params) { | |
693 bool is_enabled = (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE && | |
694 text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD); | |
695 // Only update caret position if the input method is enabled. | |
696 if (is_enabled) { | |
697 caret_rect_ = gfx::UnionRects(params.anchor_rect, params.focus_rect); | |
698 imm32_manager_->UpdateCaretRect(m_hWnd, caret_rect_); | |
699 } | |
700 } | |
701 | |
702 void RenderWidgetHostViewWin::ScrollOffsetChanged() { | |
703 } | |
704 | |
705 void RenderWidgetHostViewWin::ImeCancelComposition() { | |
706 imm32_manager_->CancelIME(m_hWnd); | |
707 } | |
708 | |
709 void RenderWidgetHostViewWin::ImeCompositionRangeChanged( | |
710 const gfx::Range& range, | |
711 const std::vector<gfx::Rect>& character_bounds) { | |
712 composition_range_ = range; | |
713 composition_character_bounds_ = character_bounds; | |
714 } | |
715 | |
716 void RenderWidgetHostViewWin::Redraw() { | |
717 RECT damage_bounds; | |
718 GetUpdateRect(&damage_bounds, FALSE); | |
719 | |
720 base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0)); | |
721 GetUpdateRgn(damage_region, FALSE); | |
722 | |
723 // Paint the invalid region synchronously. Our caller will not paint again | |
724 // until we return, so by painting to the screen here, we ensure effective | |
725 // rate-limiting of backing store updates. This helps a lot on pages that | |
726 // have animations or fairly expensive layout (e.g., google maps). | |
727 // | |
728 // We paint this window synchronously, however child windows (i.e. plugins) | |
729 // are painted asynchronously. By avoiding synchronous cross-process window | |
730 // message dispatching we allow scrolling to be smooth, and also avoid the | |
731 // browser process locking up if the plugin process is hung. | |
732 // | |
733 RedrawWindow(NULL, damage_region, RDW_UPDATENOW | RDW_NOCHILDREN); | |
734 | |
735 // Send the invalid rect in screen coordinates. | |
736 gfx::Rect invalid_screen_rect(damage_bounds); | |
737 invalid_screen_rect.Offset(GetPixelBounds().OffsetFromOrigin()); | |
738 | |
739 PaintPluginWindowsHelper(m_hWnd, invalid_screen_rect); | |
740 } | |
741 | |
742 void RenderWidgetHostViewWin::DidUpdateBackingStore( | |
743 const gfx::Rect& scroll_rect, | |
744 const gfx::Vector2d& scroll_delta, | |
745 const std::vector<gfx::Rect>& copy_rects, | |
746 const std::vector<ui::LatencyInfo>& latency_info) { | |
747 TRACE_EVENT0("content", "RenderWidgetHostViewWin::DidUpdateBackingStore"); | |
748 for (size_t i = 0; i < latency_info.size(); i++) | |
749 software_latency_info_.push_back(latency_info[i]); | |
750 if (render_widget_host_->is_hidden()) | |
751 return; | |
752 | |
753 // Schedule invalidations first so that the ScrollWindowEx call is closer to | |
754 // Redraw. That minimizes chances of "flicker" resulting if the screen | |
755 // refreshes before we have a chance to paint the exposed area. Somewhat | |
756 // surprisingly, this ordering matters. | |
757 | |
758 for (size_t i = 0; i < copy_rects.size(); ++i) { | |
759 gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(copy_rects[i]); | |
760 // Damage might not be DIP aligned. | |
761 pixel_rect.Inset(-1, -1); | |
762 RECT bounds = pixel_rect.ToRECT(); | |
763 InvalidateRect(&bounds, false); | |
764 } | |
765 | |
766 if (!scroll_rect.IsEmpty()) { | |
767 TRACE_EVENT0("content", "ScrollWindowEx"); | |
768 gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(scroll_rect); | |
769 // Damage might not be DIP aligned. | |
770 pixel_rect.Inset(-1, -1); | |
771 RECT clip_rect = pixel_rect.ToRECT(); | |
772 float scale = gfx::win::GetDeviceScaleFactor(); | |
773 int dx = static_cast<int>(scale * scroll_delta.x()); | |
774 int dy = static_cast<int>(scale * scroll_delta.y()); | |
775 ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE); | |
776 } | |
777 | |
778 if (!about_to_validate_and_paint_) | |
779 Redraw(); | |
780 } | |
781 | |
782 void RenderWidgetHostViewWin::RenderProcessGone(base::TerminationStatus status, | |
783 int error_code) { | |
784 UpdateCursorIfOverSelf(); | |
785 Destroy(); | |
786 } | |
787 | |
788 bool RenderWidgetHostViewWin::CanSubscribeFrame() const { | |
789 return render_widget_host_ != NULL; | |
790 } | |
791 | |
792 void RenderWidgetHostViewWin::WillWmDestroy() { | |
793 CleanupCompositorWindow(); | |
794 if (base::win::IsTSFAwareRequired()) | |
795 ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); | |
796 } | |
797 | |
798 void RenderWidgetHostViewWin::Destroy() { | |
799 // We've been told to destroy. | |
800 // By clearing close_on_deactivate_, we prevent further deactivations | |
801 // (caused by windows messages resulting from the DestroyWindow) from | |
802 // triggering further destructions. The deletion of this is handled by | |
803 // OnFinalMessage(); | |
804 close_on_deactivate_ = false; | |
805 render_widget_host_ = NULL; | |
806 being_destroyed_ = true; | |
807 CleanupCompositorWindow(); | |
808 | |
809 // This releases the resources associated with input scope. | |
810 UpdateInputScopeIfNecessary(ui::TEXT_INPUT_TYPE_NONE); | |
811 | |
812 if (is_fullscreen_ && win8::IsSingleWindowMetroMode()) { | |
813 MetroCloseFrameWindow close_frame_window = | |
814 reinterpret_cast<MetroCloseFrameWindow>( | |
815 ::GetProcAddress(base::win::GetMetroModule(), "CloseFrameWindow")); | |
816 DCHECK(close_frame_window); | |
817 close_frame_window(m_hWnd); | |
818 } | |
819 | |
820 DestroyWindow(); | |
821 } | |
822 | |
823 void RenderWidgetHostViewWin::SetTooltipText( | |
824 const base::string16& tooltip_text) { | |
825 if (!render_widget_host_->is_hidden()) | |
826 EnsureTooltip(); | |
827 | |
828 // Clamp the tooltip length to kMaxTooltipLength so that we don't | |
829 // accidentally DOS the user with a mega tooltip (since Windows doesn't seem | |
830 // to do this itself). | |
831 const base::string16 new_tooltip_text = | |
832 gfx::TruncateString(tooltip_text, kMaxTooltipLength); | |
833 | |
834 if (new_tooltip_text != tooltip_text_) { | |
835 tooltip_text_ = new_tooltip_text; | |
836 | |
837 // Need to check if the tooltip is already showing so that we don't | |
838 // immediately show the tooltip with no delay when we move the mouse from | |
839 // a region with no tooltip to a region with a tooltip. | |
840 if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) { | |
841 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); | |
842 ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); | |
843 } | |
844 } else { | |
845 // Make sure the tooltip gets closed after TTN_POP gets sent. For some | |
846 // reason this doesn't happen automatically, so moving the mouse around | |
847 // within the same link/image/etc doesn't cause the tooltip to re-appear. | |
848 if (!tooltip_showing_) { | |
849 if (::IsWindow(tooltip_hwnd_)) | |
850 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); | |
851 } | |
852 } | |
853 } | |
854 | |
855 BackingStore* RenderWidgetHostViewWin::AllocBackingStore( | |
856 const gfx::Size& size) { | |
857 return new BackingStoreWin(render_widget_host_, size); | |
858 } | |
859 | |
860 void RenderWidgetHostViewWin::CopyFromCompositingSurface( | |
861 const gfx::Rect& src_subrect, | |
862 const gfx::Size& dst_size, | |
863 const base::Callback<void(bool, const SkBitmap&)>& callback) { | |
864 base::ScopedClosureRunner scoped_callback_runner( | |
865 base::Bind(callback, false, SkBitmap())); | |
866 if (!accelerated_surface_) | |
867 return; | |
868 | |
869 if (dst_size.IsEmpty() || src_subrect.IsEmpty()) | |
870 return; | |
871 | |
872 ignore_result(scoped_callback_runner.Release()); | |
873 accelerated_surface_->AsyncCopyTo(src_subrect, dst_size, callback); | |
874 } | |
875 | |
876 void RenderWidgetHostViewWin::CopyFromCompositingSurfaceToVideoFrame( | |
877 const gfx::Rect& src_subrect, | |
878 const scoped_refptr<media::VideoFrame>& target, | |
879 const base::Callback<void(bool)>& callback) { | |
880 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false)); | |
881 if (!accelerated_surface_) | |
882 return; | |
883 | |
884 if (!target || (target->format() != media::VideoFrame::YV12 && | |
885 target->format() != media::VideoFrame::I420)) | |
886 return; | |
887 | |
888 if (src_subrect.IsEmpty()) | |
889 return; | |
890 | |
891 ignore_result(scoped_callback_runner.Release()); | |
892 accelerated_surface_->AsyncCopyToVideoFrame(src_subrect, target, callback); | |
893 } | |
894 | |
895 bool RenderWidgetHostViewWin::CanCopyToVideoFrame() const { | |
896 return accelerated_surface_.get() && render_widget_host_ && | |
897 render_widget_host_->is_accelerated_compositing_active(); | |
898 } | |
899 | |
900 void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) { | |
901 RenderWidgetHostViewBase::SetBackground(background); | |
902 render_widget_host_->SetBackground(background); | |
903 } | |
904 | |
905 void RenderWidgetHostViewWin::ProcessAckedTouchEvent( | |
906 const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) { | |
907 DCHECK(touch_events_enabled_); | |
908 | |
909 ScopedVector<ui::TouchEvent> events; | |
910 if (!MakeUITouchEventsFromWebTouchEvents(touch, &events, LOCAL_COORDINATES)) | |
911 return; | |
912 | |
913 ui::EventResult result = (ack_result == | |
914 INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED; | |
915 for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(), | |
916 end = events.end(); iter != end; ++iter) { | |
917 scoped_ptr<ui::GestureRecognizer::Gestures> gestures; | |
918 gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture( | |
919 *(*iter), result, this)); | |
920 ProcessGestures(gestures.get()); | |
921 } | |
922 } | |
923 | |
924 void RenderWidgetHostViewWin::UpdateDesiredTouchMode() { | |
925 // Make sure that touch events even make sense. | |
926 if (base::win::GetVersion() < base::win::VERSION_WIN7) | |
927 return; | |
928 if (touch_events_enabled_) { | |
929 CHECK(RegisterTouchWindow(m_hWnd, TWF_WANTPALM)); | |
930 } | |
931 } | |
932 | |
933 bool RenderWidgetHostViewWin::CanDispatchToConsumer( | |
934 ui::GestureConsumer* consumer) { | |
935 CHECK_EQ(static_cast<RenderWidgetHostViewWin*>(consumer), this); | |
936 return true; | |
937 } | |
938 | |
939 void RenderWidgetHostViewWin::DispatchPostponedGestureEvent( | |
940 ui::GestureEvent* event) { | |
941 ForwardGestureEventToRenderer(event); | |
942 } | |
943 | |
944 void RenderWidgetHostViewWin::DispatchCancelTouchEvent( | |
945 ui::TouchEvent* event) { | |
946 if (!render_widget_host_ || !touch_events_enabled_ || | |
947 !render_widget_host_->ShouldForwardTouchEvent()) { | |
948 return; | |
949 } | |
950 DCHECK(event->type() == blink::WebInputEvent::TouchCancel); | |
951 blink::WebTouchEvent cancel_event; | |
952 cancel_event.type = blink::WebInputEvent::TouchCancel; | |
953 cancel_event.timeStampSeconds = event->time_stamp().InSecondsF(); | |
954 render_widget_host_->ForwardTouchEventWithLatencyInfo( | |
955 cancel_event, *event->latency()); | |
956 } | |
957 | |
958 void RenderWidgetHostViewWin::SetHasHorizontalScrollbar( | |
959 bool has_horizontal_scrollbar) { | |
960 } | |
961 | |
962 void RenderWidgetHostViewWin::SetScrollOffsetPinning( | |
963 bool is_pinned_to_left, bool is_pinned_to_right) { | |
964 } | |
965 | |
966 void RenderWidgetHostViewWin::SetCompositionText( | |
967 const ui::CompositionText& composition) { | |
968 if (!base::win::IsTSFAwareRequired()) { | |
969 NOTREACHED(); | |
970 return; | |
971 } | |
972 if (!render_widget_host_) | |
973 return; | |
974 // ui::CompositionUnderline should be identical to | |
975 // blink::WebCompositionUnderline, so that we can do reinterpret_cast safely. | |
976 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) == | |
977 sizeof(blink::WebCompositionUnderline), | |
978 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); | |
979 const std::vector<blink::WebCompositionUnderline>& underlines = | |
980 reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>( | |
981 composition.underlines); | |
982 render_widget_host_->ImeSetComposition(composition.text, underlines, | |
983 composition.selection.end(), | |
984 composition.selection.end()); | |
985 } | |
986 | |
987 void RenderWidgetHostViewWin::ConfirmCompositionText() { | |
988 if (!base::win::IsTSFAwareRequired()) { | |
989 NOTREACHED(); | |
990 return; | |
991 } | |
992 // TODO(nona): Implement this function. | |
993 NOTIMPLEMENTED(); | |
994 } | |
995 | |
996 void RenderWidgetHostViewWin::ClearCompositionText() { | |
997 if (!base::win::IsTSFAwareRequired()) { | |
998 NOTREACHED(); | |
999 return; | |
1000 } | |
1001 // TODO(nona): Implement this function. | |
1002 NOTIMPLEMENTED(); | |
1003 } | |
1004 | |
1005 void RenderWidgetHostViewWin::InsertText(const base::string16& text) { | |
1006 if (!base::win::IsTSFAwareRequired()) { | |
1007 NOTREACHED(); | |
1008 return; | |
1009 } | |
1010 if (render_widget_host_) | |
1011 render_widget_host_->ImeConfirmComposition(text, | |
1012 gfx::Range::InvalidRange(), | |
1013 false); | |
1014 } | |
1015 | |
1016 void RenderWidgetHostViewWin::InsertChar(base::char16 ch, int flags) { | |
1017 if (!base::win::IsTSFAwareRequired()) { | |
1018 NOTREACHED(); | |
1019 return; | |
1020 } | |
1021 // TODO(nona): Implement this function. | |
1022 NOTIMPLEMENTED(); | |
1023 } | |
1024 | |
1025 gfx::NativeWindow RenderWidgetHostViewWin::GetAttachedWindow() const { | |
1026 return m_hWnd; | |
1027 } | |
1028 | |
1029 ui::TextInputType RenderWidgetHostViewWin::GetTextInputType() const { | |
1030 if (!base::win::IsTSFAwareRequired()) { | |
1031 NOTREACHED(); | |
1032 return ui::TEXT_INPUT_TYPE_NONE; | |
1033 } | |
1034 return text_input_type_; | |
1035 } | |
1036 | |
1037 ui::TextInputMode RenderWidgetHostViewWin::GetTextInputMode() const { | |
1038 if (!base::win::IsTSFAwareRequired()) { | |
1039 NOTREACHED(); | |
1040 return ui::TEXT_INPUT_MODE_DEFAULT; | |
1041 } | |
1042 return text_input_mode_; | |
1043 } | |
1044 | |
1045 bool RenderWidgetHostViewWin::CanComposeInline() const { | |
1046 if (!base::win::IsTSFAwareRequired()) { | |
1047 NOTREACHED(); | |
1048 return false; | |
1049 } | |
1050 // TODO(nona): Implement this function. | |
1051 NOTIMPLEMENTED(); | |
1052 return false; | |
1053 } | |
1054 | |
1055 gfx::Rect RenderWidgetHostViewWin::GetCaretBounds() const { | |
1056 if (!base::win::IsTSFAwareRequired()) { | |
1057 NOTREACHED(); | |
1058 return gfx::Rect(0, 0, 0, 0); | |
1059 } | |
1060 RECT tmp_rect = caret_rect_.ToRECT(); | |
1061 ClientToScreen(&tmp_rect); | |
1062 return gfx::Rect(tmp_rect); | |
1063 } | |
1064 | |
1065 bool RenderWidgetHostViewWin::GetCompositionCharacterBounds( | |
1066 uint32 index, gfx::Rect* rect) const { | |
1067 if (!base::win::IsTSFAwareRequired()) { | |
1068 NOTREACHED(); | |
1069 return false; | |
1070 } | |
1071 DCHECK(rect); | |
1072 if (index >= composition_character_bounds_.size()) | |
1073 return false; | |
1074 RECT rec = composition_character_bounds_[index].ToRECT(); | |
1075 ClientToScreen(&rec); | |
1076 *rect = gfx::Rect(rec); | |
1077 return true; | |
1078 } | |
1079 | |
1080 bool RenderWidgetHostViewWin::HasCompositionText() const { | |
1081 if (!base::win::IsTSFAwareRequired()) { | |
1082 NOTREACHED(); | |
1083 return false; | |
1084 } | |
1085 // TODO(nona): Implement this function. | |
1086 NOTIMPLEMENTED(); | |
1087 return false; | |
1088 } | |
1089 | |
1090 bool RenderWidgetHostViewWin::GetTextRange(gfx::Range* range) const { | |
1091 if (!base::win::IsTSFAwareRequired()) { | |
1092 NOTREACHED(); | |
1093 return false; | |
1094 } | |
1095 range->set_start(selection_text_offset_); | |
1096 range->set_end(selection_text_offset_ + selection_text_.length()); | |
1097 return false; | |
1098 } | |
1099 | |
1100 bool RenderWidgetHostViewWin::GetCompositionTextRange(gfx::Range* range) const { | |
1101 if (!base::win::IsTSFAwareRequired()) { | |
1102 NOTREACHED(); | |
1103 return false; | |
1104 } | |
1105 // TODO(nona): Implement this function. | |
1106 NOTIMPLEMENTED(); | |
1107 return false; | |
1108 } | |
1109 | |
1110 bool RenderWidgetHostViewWin::GetSelectionRange(gfx::Range* range) const { | |
1111 if (!base::win::IsTSFAwareRequired()) { | |
1112 NOTREACHED(); | |
1113 return false; | |
1114 } | |
1115 range->set_start(selection_range_.start()); | |
1116 range->set_end(selection_range_.end()); | |
1117 return false; | |
1118 } | |
1119 | |
1120 bool RenderWidgetHostViewWin::SetSelectionRange(const gfx::Range& range) { | |
1121 if (!base::win::IsTSFAwareRequired()) { | |
1122 NOTREACHED(); | |
1123 return false; | |
1124 } | |
1125 // TODO(nona): Implement this function. | |
1126 NOTIMPLEMENTED(); | |
1127 return false; | |
1128 } | |
1129 | |
1130 bool RenderWidgetHostViewWin::DeleteRange(const gfx::Range& range) { | |
1131 if (!base::win::IsTSFAwareRequired()) { | |
1132 NOTREACHED(); | |
1133 return false; | |
1134 } | |
1135 // TODO(nona): Implement this function. | |
1136 NOTIMPLEMENTED(); | |
1137 return false; | |
1138 } | |
1139 | |
1140 bool RenderWidgetHostViewWin::GetTextFromRange(const gfx::Range& range, | |
1141 base::string16* text) const { | |
1142 if (!base::win::IsTSFAwareRequired()) { | |
1143 NOTREACHED(); | |
1144 return false; | |
1145 } | |
1146 gfx::Range selection_text_range(selection_text_offset_, | |
1147 selection_text_offset_ + selection_text_.length()); | |
1148 if (!selection_text_range.Contains(range)) { | |
1149 text->clear(); | |
1150 return false; | |
1151 } | |
1152 if (selection_text_range.EqualsIgnoringDirection(range)) { | |
1153 *text = selection_text_; | |
1154 } else { | |
1155 *text = selection_text_.substr( | |
1156 range.GetMin() - selection_text_offset_, | |
1157 range.length()); | |
1158 } | |
1159 return true; | |
1160 } | |
1161 | |
1162 void RenderWidgetHostViewWin::OnInputMethodChanged() { | |
1163 if (!base::win::IsTSFAwareRequired()) { | |
1164 NOTREACHED(); | |
1165 return; | |
1166 } | |
1167 // TODO(nona): Implement this function. | |
1168 NOTIMPLEMENTED(); | |
1169 } | |
1170 | |
1171 bool RenderWidgetHostViewWin::ChangeTextDirectionAndLayoutAlignment( | |
1172 base::i18n::TextDirection direction) { | |
1173 if (!base::win::IsTSFAwareRequired()) { | |
1174 NOTREACHED(); | |
1175 return false; | |
1176 } | |
1177 // TODO(nona): Implement this function. | |
1178 NOTIMPLEMENTED(); | |
1179 return false; | |
1180 } | |
1181 | |
1182 void RenderWidgetHostViewWin::ExtendSelectionAndDelete( | |
1183 size_t before, | |
1184 size_t after) { | |
1185 if (!base::win::IsTSFAwareRequired()) { | |
1186 NOTREACHED(); | |
1187 return; | |
1188 } | |
1189 if (!render_widget_host_) | |
1190 return; | |
1191 render_widget_host_->ExtendSelectionAndDelete(before, after); | |
1192 } | |
1193 | |
1194 void RenderWidgetHostViewWin::EnsureCaretInRect(const gfx::Rect& rect) { | |
1195 // TODO(nona): Implement this function. | |
1196 NOTIMPLEMENTED(); | |
1197 } | |
1198 | |
1199 void RenderWidgetHostViewWin::OnCandidateWindowShown() { | |
1200 } | |
1201 | |
1202 void RenderWidgetHostViewWin::OnCandidateWindowUpdated() { | |
1203 } | |
1204 | |
1205 void RenderWidgetHostViewWin::OnCandidateWindowHidden() { | |
1206 } | |
1207 | |
1208 /////////////////////////////////////////////////////////////////////////////// | |
1209 // RenderWidgetHostViewWin, private: | |
1210 | |
1211 LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) { | |
1212 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCreate"); | |
1213 // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale | |
1214 // of a browser process. | |
1215 OnInputLangChange(0, 0); | |
1216 // Marks that window as supporting mouse-wheel messages rerouting so it is | |
1217 // scrolled when under the mouse pointer even if inactive. | |
1218 props_.push_back(ui::SetWindowSupportsRerouteMouseWheel(m_hWnd)); | |
1219 | |
1220 WTSRegisterSessionNotification(m_hWnd, NOTIFY_FOR_THIS_SESSION); | |
1221 | |
1222 UpdateDesiredTouchMode(); | |
1223 UpdateIMEState(); | |
1224 | |
1225 return 0; | |
1226 } | |
1227 | |
1228 void RenderWidgetHostViewWin::OnActivate(UINT action, BOOL minimized, | |
1229 HWND window) { | |
1230 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnActivate"); | |
1231 // If the container is a popup, clicking elsewhere on screen should close the | |
1232 // popup. | |
1233 if (close_on_deactivate_ && action == WA_INACTIVE) { | |
1234 // Send a windows message so that any derived classes | |
1235 // will get a change to override the default handling | |
1236 SendMessage(WM_CANCELMODE); | |
1237 } | |
1238 } | |
1239 | |
1240 void RenderWidgetHostViewWin::OnDestroy() { | |
1241 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnDestroy"); | |
1242 DetachPluginsHelper(m_hWnd); | |
1243 | |
1244 props_.clear(); | |
1245 | |
1246 if (base::win::GetVersion() >= base::win::VERSION_WIN7 && | |
1247 IsTouchWindow(m_hWnd, NULL)) { | |
1248 UnregisterTouchWindow(m_hWnd); | |
1249 } | |
1250 | |
1251 CleanupCompositorWindow(); | |
1252 | |
1253 WTSUnRegisterSessionNotification(m_hWnd); | |
1254 | |
1255 ResetTooltip(); | |
1256 TrackMouseLeave(false); | |
1257 } | |
1258 | |
1259 void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) { | |
1260 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnPaint"); | |
1261 | |
1262 // Grab the region to paint before creation of paint_dc since it clears the | |
1263 // damage region. | |
1264 base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0)); | |
1265 GetUpdateRgn(damage_region, FALSE); | |
1266 | |
1267 CPaintDC paint_dc(m_hWnd); | |
1268 | |
1269 if (!render_widget_host_) | |
1270 return; | |
1271 | |
1272 DCHECK(render_widget_host_->GetProcess()->HasConnection()); | |
1273 | |
1274 // If the GPU process is rendering to a child window, compositing is | |
1275 // already triggered by damage to compositor_host_window_, so all we need to | |
1276 // do here is clear borders during resize. | |
1277 if (compositor_host_window_ && | |
1278 render_widget_host_->is_accelerated_compositing_active()) { | |
1279 RECT host_rect, child_rect; | |
1280 GetClientRect(&host_rect); | |
1281 if (::GetClientRect(compositor_host_window_, &child_rect) && | |
1282 (child_rect.right < host_rect.right || | |
1283 child_rect.bottom < host_rect.bottom)) { | |
1284 paint_dc.FillRect(&host_rect, | |
1285 reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH))); | |
1286 } | |
1287 return; | |
1288 } | |
1289 | |
1290 if (accelerated_surface_.get() && | |
1291 render_widget_host_->is_accelerated_compositing_active()) { | |
1292 AcceleratedPaint(paint_dc.m_hDC); | |
1293 return; | |
1294 } | |
1295 | |
1296 about_to_validate_and_paint_ = true; | |
1297 BackingStoreWin* backing_store = static_cast<BackingStoreWin*>( | |
1298 render_widget_host_->GetBackingStore(true)); | |
1299 | |
1300 // We initialize |paint_dc| (and thus call BeginPaint()) after calling | |
1301 // GetBackingStore(), so that if it updates the invalid rect we'll catch the | |
1302 // changes and repaint them. | |
1303 about_to_validate_and_paint_ = false; | |
1304 | |
1305 if (compositor_host_window_ && hide_compositor_window_at_next_paint_) { | |
1306 ::ShowWindow(compositor_host_window_, SW_HIDE); | |
1307 hide_compositor_window_at_next_paint_ = false; | |
1308 } | |
1309 | |
1310 gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint); | |
1311 if (damaged_rect.IsEmpty()) | |
1312 return; | |
1313 | |
1314 if (backing_store) { | |
1315 gfx::Rect bitmap_rect(gfx::Point(), | |
1316 gfx::win::DIPToScreenSize(backing_store->size())); | |
1317 | |
1318 bool manage_colors = BackingStoreWin::ColorManagementEnabled(); | |
1319 if (manage_colors) | |
1320 SetICMMode(paint_dc.m_hDC, ICM_ON); | |
1321 | |
1322 // Blit only the damaged regions from the backing store. | |
1323 DWORD data_size = GetRegionData(damage_region, 0, NULL); | |
1324 scoped_ptr<char[]> region_data_buf; | |
1325 RGNDATA* region_data = NULL; | |
1326 RECT* region_rects = NULL; | |
1327 | |
1328 if (data_size) { | |
1329 region_data_buf.reset(new char[data_size]); | |
1330 region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get()); | |
1331 region_rects = reinterpret_cast<RECT*>(region_data->Buffer); | |
1332 data_size = GetRegionData(damage_region, data_size, region_data); | |
1333 } | |
1334 | |
1335 if (!data_size) { | |
1336 // Grabbing the damaged regions failed, fake with the whole rect. | |
1337 data_size = sizeof(RGNDATAHEADER) + sizeof(RECT); | |
1338 region_data_buf.reset(new char[data_size]); | |
1339 region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get()); | |
1340 region_rects = reinterpret_cast<RECT*>(region_data->Buffer); | |
1341 region_data->rdh.nCount = 1; | |
1342 region_rects[0] = damaged_rect.ToRECT(); | |
1343 } | |
1344 | |
1345 for (DWORD i = 0; i < region_data->rdh.nCount; ++i) { | |
1346 gfx::Rect paint_rect = | |
1347 gfx::IntersectRects(bitmap_rect, gfx::Rect(region_rects[i])); | |
1348 if (!paint_rect.IsEmpty()) { | |
1349 BitBlt(paint_dc.m_hDC, | |
1350 paint_rect.x(), | |
1351 paint_rect.y(), | |
1352 paint_rect.width(), | |
1353 paint_rect.height(), | |
1354 backing_store->hdc(), | |
1355 paint_rect.x(), | |
1356 paint_rect.y(), | |
1357 SRCCOPY); | |
1358 } | |
1359 } | |
1360 | |
1361 if (manage_colors) | |
1362 SetICMMode(paint_dc.m_hDC, ICM_OFF); | |
1363 | |
1364 // Fill the remaining portion of the damaged_rect with the background | |
1365 if (damaged_rect.right() > bitmap_rect.right()) { | |
1366 RECT r; | |
1367 r.left = std::max(bitmap_rect.right(), damaged_rect.x()); | |
1368 r.right = damaged_rect.right(); | |
1369 r.top = damaged_rect.y(); | |
1370 r.bottom = std::min(bitmap_rect.bottom(), damaged_rect.bottom()); | |
1371 DrawBackground(r, &paint_dc); | |
1372 } | |
1373 if (damaged_rect.bottom() > bitmap_rect.bottom()) { | |
1374 RECT r; | |
1375 r.left = damaged_rect.x(); | |
1376 r.right = damaged_rect.right(); | |
1377 r.top = std::max(bitmap_rect.bottom(), damaged_rect.y()); | |
1378 r.bottom = damaged_rect.bottom(); | |
1379 DrawBackground(r, &paint_dc); | |
1380 } | |
1381 if (!whiteout_start_time_.is_null()) { | |
1382 TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_; | |
1383 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); | |
1384 | |
1385 // Reset the start time to 0 so that we start recording again the next | |
1386 // time the backing store is NULL... | |
1387 whiteout_start_time_ = TimeTicks(); | |
1388 } | |
1389 if (!web_contents_switch_paint_time_.is_null()) { | |
1390 TimeDelta web_contents_switch_paint_duration = TimeTicks::Now() - | |
1391 web_contents_switch_paint_time_; | |
1392 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", | |
1393 web_contents_switch_paint_duration); | |
1394 // Reset contents_switch_paint_time_ to 0 so future tab selections are | |
1395 // recorded. | |
1396 web_contents_switch_paint_time_ = TimeTicks(); | |
1397 } | |
1398 | |
1399 for (size_t i = 0; i < software_latency_info_.size(); i++) { | |
1400 software_latency_info_[i].AddLatencyNumber( | |
1401 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0); | |
1402 render_widget_host_->FrameSwapped(software_latency_info_[i]); | |
1403 } | |
1404 software_latency_info_.clear(); | |
1405 } else { | |
1406 DrawBackground(paint_dc.m_ps.rcPaint, &paint_dc); | |
1407 if (whiteout_start_time_.is_null()) | |
1408 whiteout_start_time_ = TimeTicks::Now(); | |
1409 } | |
1410 } | |
1411 | |
1412 void RenderWidgetHostViewWin::DrawBackground(const RECT& dirty_rect, | |
1413 CPaintDC* dc) { | |
1414 if (!background_.empty()) { | |
1415 gfx::Rect dirty_area(dirty_rect); | |
1416 gfx::Canvas canvas(dirty_area.size(), 1.0f, true); | |
1417 canvas.Translate(-dirty_area.OffsetFromOrigin()); | |
1418 | |
1419 gfx::Rect dc_rect(dc->m_ps.rcPaint); | |
1420 // TODO(pkotwicz): Fix |background_| such that it is an ImageSkia. | |
1421 canvas.TileImageInt(gfx::ImageSkia::CreateFrom1xBitmap(background_), | |
1422 0, 0, dc_rect.width(), dc_rect.height()); | |
1423 | |
1424 skia::DrawToNativeContext(canvas.sk_canvas(), *dc, dirty_area.x(), | |
1425 dirty_area.y(), NULL); | |
1426 } else { | |
1427 HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); | |
1428 dc->FillRect(&dirty_rect, white_brush); | |
1429 } | |
1430 } | |
1431 | |
1432 void RenderWidgetHostViewWin::OnNCPaint(HRGN update_region) { | |
1433 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNCPaint"); | |
1434 // Do nothing. This suppresses the resize corner that Windows would | |
1435 // otherwise draw for us. | |
1436 } | |
1437 | |
1438 void RenderWidgetHostViewWin::SetClickthroughRegion(SkRegion* region) { | |
1439 transparent_region_.reset(region); | |
1440 } | |
1441 | |
1442 LRESULT RenderWidgetHostViewWin::OnNCHitTest(const CPoint& point) { | |
1443 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNCHitTest"); | |
1444 RECT rc; | |
1445 GetWindowRect(&rc); | |
1446 if (transparent_region_.get() && | |
1447 transparent_region_->contains(point.x - rc.left, point.y - rc.top)) { | |
1448 SetMsgHandled(TRUE); | |
1449 return HTTRANSPARENT; | |
1450 } | |
1451 SetMsgHandled(FALSE); | |
1452 return 0; | |
1453 } | |
1454 | |
1455 LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) { | |
1456 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnEraseBkgnd"); | |
1457 return 1; | |
1458 } | |
1459 | |
1460 LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code, | |
1461 UINT mouse_message_id) { | |
1462 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSetCursor"); | |
1463 UpdateCursorIfOverSelf(); | |
1464 return 0; | |
1465 } | |
1466 | |
1467 void RenderWidgetHostViewWin::OnSetFocus(HWND window) { | |
1468 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSetFocus"); | |
1469 if (!render_widget_host_) | |
1470 return; | |
1471 | |
1472 if (GetBrowserAccessibilityManager()) | |
1473 GetBrowserAccessibilityManager()->GotFocus(pointer_down_context_); | |
1474 | |
1475 render_widget_host_->GotFocus(); | |
1476 render_widget_host_->SetActive(true); | |
1477 | |
1478 if (base::win::IsTSFAwareRequired()) | |
1479 ui::TSFBridge::GetInstance()->SetFocusedClient(m_hWnd, this); | |
1480 } | |
1481 | |
1482 void RenderWidgetHostViewWin::OnKillFocus(HWND window) { | |
1483 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnKillFocus"); | |
1484 if (!render_widget_host_) | |
1485 return; | |
1486 | |
1487 render_widget_host_->SetActive(false); | |
1488 render_widget_host_->Blur(); | |
1489 | |
1490 last_touch_location_ = gfx::Point(-1, -1); | |
1491 | |
1492 if (base::win::IsTSFAwareRequired()) | |
1493 ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); | |
1494 } | |
1495 | |
1496 void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) { | |
1497 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCaptureChanged"); | |
1498 if (render_widget_host_) | |
1499 render_widget_host_->LostCapture(); | |
1500 pointer_down_context_ = false; | |
1501 } | |
1502 | |
1503 void RenderWidgetHostViewWin::OnCancelMode() { | |
1504 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnCancelMode"); | |
1505 if (render_widget_host_) | |
1506 render_widget_host_->LostCapture(); | |
1507 | |
1508 if ((is_fullscreen_ || close_on_deactivate_) && | |
1509 !weak_factory_.HasWeakPtrs()) { | |
1510 // Dismiss popups and menus. We do this asynchronously to avoid changing | |
1511 // activation within this callstack, which may interfere with another window | |
1512 // being activated. We can synchronously hide the window, but we need to | |
1513 // not change activation while doing so. | |
1514 SetWindowPos(NULL, 0, 0, 0, 0, | |
1515 SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | | |
1516 SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); | |
1517 base::MessageLoop::current()->PostTask( | |
1518 FROM_HERE, | |
1519 base::Bind(&RenderWidgetHostViewWin::ShutdownHost, | |
1520 weak_factory_.GetWeakPtr())); | |
1521 } | |
1522 } | |
1523 | |
1524 void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set, | |
1525 HKL input_language_id) { | |
1526 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnInputLangChange"); | |
1527 // Send the given Locale ID to the IMM32Manager object and retrieves whether | |
1528 // or not the current input context has IMEs. | |
1529 // If the current input context has IMEs, a browser process has to send a | |
1530 // request to a renderer process that it needs status messages about | |
1531 // the focused edit control from the renderer process. | |
1532 // On the other hand, if the current input context does not have IMEs, the | |
1533 // browser process also has to send a request to the renderer process that | |
1534 // it does not need the status messages any longer. | |
1535 // To minimize the number of this notification request, we should check if | |
1536 // the browser process is actually retrieving the status messages (this | |
1537 // state is stored in ime_notification_) and send a request only if the | |
1538 // browser process has to update this status, its details are listed below: | |
1539 // * If a browser process is not retrieving the status messages, | |
1540 // (i.e. ime_notification_ == false), | |
1541 // send this request only if the input context does have IMEs, | |
1542 // (i.e. ime_status == true); | |
1543 // When it successfully sends the request, toggle its notification status, | |
1544 // (i.e.ime_notification_ = !ime_notification_ = true). | |
1545 // * If a browser process is retrieving the status messages | |
1546 // (i.e. ime_notification_ == true), | |
1547 // send this request only if the input context does not have IMEs, | |
1548 // (i.e. ime_status == false). | |
1549 // When it successfully sends the request, toggle its notification status, | |
1550 // (i.e.ime_notification_ = !ime_notification_ = false). | |
1551 // To analyze the above actions, we can optimize them into the ones | |
1552 // listed below: | |
1553 // 1 Sending a request only if ime_status_ != ime_notification_, and; | |
1554 // 2 Copying ime_status to ime_notification_ if it sends the request | |
1555 // successfully (because Action 1 shows ime_status = !ime_notification_.) | |
1556 bool ime_status = imm32_manager_->SetInputLanguage(); | |
1557 if (ime_status != ime_notification_) { | |
1558 if (render_widget_host_) { | |
1559 render_widget_host_->SetInputMethodActive(ime_status); | |
1560 ime_notification_ = ime_status; | |
1561 } | |
1562 } | |
1563 // Call DefWindowProc() for consistency with other Chrome windows. | |
1564 // TODO(hbono): This is a speculative fix for Bug 36354 and this code may be | |
1565 // reverted if it does not fix it. | |
1566 SetMsgHandled(FALSE); | |
1567 } | |
1568 | |
1569 void RenderWidgetHostViewWin::OnThemeChanged() { | |
1570 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnThemeChanged"); | |
1571 if (!render_widget_host_) | |
1572 return; | |
1573 render_widget_host_->Send(new ViewMsg_ThemeChanged( | |
1574 render_widget_host_->GetRoutingID())); | |
1575 } | |
1576 | |
1577 LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) { | |
1578 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnNotify"); | |
1579 if (tooltip_hwnd_ == NULL) | |
1580 return 0; | |
1581 | |
1582 switch (header->code) { | |
1583 case TTN_GETDISPINFO: { | |
1584 NMTTDISPINFOW* tooltip_info = reinterpret_cast<NMTTDISPINFOW*>(header); | |
1585 tooltip_info->szText[0] = L'\0'; | |
1586 tooltip_info->lpszText = const_cast<WCHAR*>(tooltip_text_.c_str()); | |
1587 ::SendMessage( | |
1588 tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels); | |
1589 SetMsgHandled(TRUE); | |
1590 break; | |
1591 } | |
1592 case TTN_POP: | |
1593 tooltip_showing_ = false; | |
1594 SetMsgHandled(TRUE); | |
1595 break; | |
1596 case TTN_SHOW: | |
1597 // Tooltip shouldn't be shown when the mouse is locked. | |
1598 DCHECK(!mouse_locked_); | |
1599 tooltip_showing_ = true; | |
1600 SetMsgHandled(TRUE); | |
1601 break; | |
1602 } | |
1603 return 0; | |
1604 } | |
1605 | |
1606 LRESULT RenderWidgetHostViewWin::OnImeSetContext( | |
1607 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
1608 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeSetContext"); | |
1609 if (!render_widget_host_) | |
1610 return 0; | |
1611 | |
1612 // We need status messages about the focused input control from a | |
1613 // renderer process when: | |
1614 // * the current input context has IMEs, and; | |
1615 // * an application is activated. | |
1616 // This seems to tell we should also check if the current input context has | |
1617 // IMEs before sending a request, however, this WM_IME_SETCONTEXT is | |
1618 // fortunately sent to an application only while the input context has IMEs. | |
1619 // Therefore, we just start/stop status messages according to the activation | |
1620 // status of this application without checks. | |
1621 bool activated = (wparam == TRUE); | |
1622 if (render_widget_host_) { | |
1623 render_widget_host_->SetInputMethodActive(activated); | |
1624 ime_notification_ = activated; | |
1625 } | |
1626 | |
1627 if (ime_notification_) | |
1628 imm32_manager_->CreateImeWindow(m_hWnd); | |
1629 | |
1630 imm32_manager_->CleanupComposition(m_hWnd); | |
1631 return imm32_manager_->SetImeWindowStyle( | |
1632 m_hWnd, message, wparam, lparam, &handled); | |
1633 } | |
1634 | |
1635 LRESULT RenderWidgetHostViewWin::OnImeStartComposition( | |
1636 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
1637 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeStartComposition"); | |
1638 if (!render_widget_host_) | |
1639 return 0; | |
1640 | |
1641 // Reset the composition status and create IME windows. | |
1642 imm32_manager_->CreateImeWindow(m_hWnd); | |
1643 imm32_manager_->ResetComposition(m_hWnd); | |
1644 // When the focus is on an element that does not draw composition by itself | |
1645 // (i.e., PPAPI plugin not handling IME), let IME to draw the text. Otherwise | |
1646 // we have to prevent WTL from calling ::DefWindowProc() because the function | |
1647 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to | |
1648 // over-write the position of IME windows. | |
1649 handled = (can_compose_inline_ ? TRUE : FALSE); | |
1650 return 0; | |
1651 } | |
1652 | |
1653 LRESULT RenderWidgetHostViewWin::OnImeComposition( | |
1654 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
1655 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeComposition"); | |
1656 if (!render_widget_host_) | |
1657 return 0; | |
1658 | |
1659 // At first, update the position of the IME window. | |
1660 imm32_manager_->UpdateImeWindow(m_hWnd); | |
1661 | |
1662 // ui::CompositionUnderline should be identical to | |
1663 // blink::WebCompositionUnderline, so that we can do reinterpret_cast safely. | |
1664 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) == | |
1665 sizeof(blink::WebCompositionUnderline), | |
1666 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); | |
1667 | |
1668 // Retrieve the result string and its attributes of the ongoing composition | |
1669 // and send it to a renderer process. | |
1670 ui::CompositionText composition; | |
1671 if (imm32_manager_->GetResult(m_hWnd, lparam, &composition.text)) { | |
1672 render_widget_host_->ImeConfirmComposition( | |
1673 composition.text, gfx::Range::InvalidRange(), false); | |
1674 imm32_manager_->ResetComposition(m_hWnd); | |
1675 // Fall though and try reading the composition string. | |
1676 // Japanese IMEs send a message containing both GCS_RESULTSTR and | |
1677 // GCS_COMPSTR, which means an ongoing composition has been finished | |
1678 // by the start of another composition. | |
1679 } | |
1680 // Retrieve the composition string and its attributes of the ongoing | |
1681 // composition and send it to a renderer process. | |
1682 if (imm32_manager_->GetComposition(m_hWnd, lparam, &composition)) { | |
1683 // TODO(suzhe): due to a bug of webkit, we can't use selection range with | |
1684 // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788 | |
1685 composition.selection = gfx::Range(composition.selection.end()); | |
1686 | |
1687 // TODO(suzhe): convert both renderer_host and renderer to use | |
1688 // ui::CompositionText. | |
1689 const std::vector<blink::WebCompositionUnderline>& underlines = | |
1690 reinterpret_cast<const std::vector<blink::WebCompositionUnderline>&>( | |
1691 composition.underlines); | |
1692 render_widget_host_->ImeSetComposition( | |
1693 composition.text, underlines, | |
1694 composition.selection.start(), composition.selection.end()); | |
1695 } | |
1696 // We have to prevent WTL from calling ::DefWindowProc() because we do not | |
1697 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. | |
1698 handled = TRUE; | |
1699 if (!can_compose_inline_) { | |
1700 // When the focus is on an element that does not draw composition by itself | |
1701 // (i.e., PPAPI plugin not handling IME), let IME to draw the text, which | |
1702 // is the default behavior of DefWindowProc. Note, however, even in this | |
1703 // case we don't want GCS_RESULTSTR to be converted to WM_IME_CHAR messages. | |
1704 // Thus we explicitly drop the flag. | |
1705 return ::DefWindowProc(m_hWnd, message, wparam, lparam & ~GCS_RESULTSTR); | |
1706 } | |
1707 return 0; | |
1708 } | |
1709 | |
1710 LRESULT RenderWidgetHostViewWin::OnImeEndComposition( | |
1711 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
1712 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeEndComposition"); | |
1713 if (!render_widget_host_) | |
1714 return 0; | |
1715 | |
1716 if (imm32_manager_->is_composing()) { | |
1717 // A composition has been ended while there is an ongoing composition, | |
1718 // i.e. the ongoing composition has been canceled. | |
1719 // We need to reset the composition status both of the IMM32Manager object | |
1720 // and of the renderer process. | |
1721 render_widget_host_->ImeCancelComposition(); | |
1722 imm32_manager_->ResetComposition(m_hWnd); | |
1723 } | |
1724 imm32_manager_->DestroyImeWindow(m_hWnd); | |
1725 // Let WTL call ::DefWindowProc() and release its resources. | |
1726 handled = FALSE; | |
1727 return 0; | |
1728 } | |
1729 | |
1730 LRESULT RenderWidgetHostViewWin::OnImeRequest( | |
1731 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
1732 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnImeRequest"); | |
1733 if (!render_widget_host_) { | |
1734 handled = FALSE; | |
1735 return 0; | |
1736 } | |
1737 | |
1738 // Should not receive WM_IME_REQUEST message, if IME is disabled. | |
1739 if (text_input_type_ == ui::TEXT_INPUT_TYPE_NONE || | |
1740 text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) { | |
1741 handled = FALSE; | |
1742 return 0; | |
1743 } | |
1744 | |
1745 switch (wparam) { | |
1746 case IMR_RECONVERTSTRING: | |
1747 return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam)); | |
1748 case IMR_DOCUMENTFEED: | |
1749 return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam)); | |
1750 case IMR_QUERYCHARPOSITION: | |
1751 return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam)); | |
1752 default: | |
1753 handled = FALSE; | |
1754 return 0; | |
1755 } | |
1756 } | |
1757 | |
1758 LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam, | |
1759 LPARAM lparam, BOOL& handled) { | |
1760 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnMouseEvent"); | |
1761 handled = TRUE; | |
1762 | |
1763 if (message == WM_MOUSELEAVE) | |
1764 ignore_mouse_movement_ = true; | |
1765 | |
1766 if (mouse_locked_) { | |
1767 HandleLockedMouseEvent(message, wparam, lparam); | |
1768 MoveCursorToCenterIfNecessary(); | |
1769 return 0; | |
1770 } | |
1771 | |
1772 if (::IsWindow(tooltip_hwnd_)) { | |
1773 // Forward mouse events through to the tooltip window | |
1774 MSG msg; | |
1775 msg.hwnd = m_hWnd; | |
1776 msg.message = message; | |
1777 msg.wParam = wparam; | |
1778 msg.lParam = lparam; | |
1779 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, NULL, | |
1780 reinterpret_cast<LPARAM>(&msg)); | |
1781 } | |
1782 | |
1783 // Due to a bug in Windows, the simulated mouse events for a touch event | |
1784 // outside our bounds are delivered to us if we were previously focused | |
1785 // causing crbug.com/159982. As a workaround, we check if this event is a | |
1786 // simulated mouse event outside our bounds, and if so, we send it to the | |
1787 // right window. | |
1788 if ((message == WM_LBUTTONDOWN || message == WM_LBUTTONUP) && | |
1789 ui::IsMouseEventFromTouch(message)) { | |
1790 CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); | |
1791 ClientToScreen(&cursor_pos); | |
1792 if (!GetPixelBounds().Contains(cursor_pos.x, cursor_pos.y)) { | |
1793 HWND window = WindowFromPoint(cursor_pos); | |
1794 if (window) { | |
1795 LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0, | |
1796 MAKELPARAM(cursor_pos.x, cursor_pos.y)); | |
1797 const bool in_client_area = (nc_hit_result == HTCLIENT); | |
1798 int event_type; | |
1799 if (message == WM_LBUTTONDOWN) | |
1800 event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; | |
1801 else | |
1802 event_type = in_client_area ? WM_LBUTTONUP : WM_NCLBUTTONUP; | |
1803 | |
1804 // Convert the coordinates to the target window. | |
1805 RECT window_bounds; | |
1806 ::GetWindowRect(window, &window_bounds); | |
1807 int window_x = cursor_pos.x - window_bounds.left; | |
1808 int window_y = cursor_pos.y - window_bounds.top; | |
1809 if (in_client_area) { | |
1810 ::PostMessage(window, event_type, wparam, | |
1811 MAKELPARAM(window_x, window_y)); | |
1812 } else { | |
1813 ::PostMessage(window, event_type, nc_hit_result, | |
1814 MAKELPARAM(cursor_pos.x, cursor_pos.y)); | |
1815 } | |
1816 return 0; | |
1817 } | |
1818 } | |
1819 } | |
1820 | |
1821 // TODO(jcampan): I am not sure if we should forward the message to the | |
1822 // WebContentsImpl first in the case of popups. If we do, we would need to | |
1823 // convert the click from the popup window coordinates to the WebContentsImpl' | |
1824 // window coordinates. For now we don't forward the message in that case to | |
1825 // address bug #907474. | |
1826 // Note: GetParent() on popup windows returns the top window and not the | |
1827 // parent the window was created with (the parent and the owner of the popup | |
1828 // is the first non-child view of the view that was specified to the create | |
1829 // call). So the WebContentsImpl's window would have to be specified to the | |
1830 // RenderViewHostHWND as there is no way to retrieve it from the HWND. | |
1831 | |
1832 // Don't forward if the container is a popup or fullscreen widget. | |
1833 if (!is_fullscreen_ && !close_on_deactivate_) { | |
1834 switch (message) { | |
1835 case WM_LBUTTONDOWN: | |
1836 case WM_MBUTTONDOWN: | |
1837 case WM_RBUTTONDOWN: | |
1838 // Finish the ongoing composition whenever a mouse click happens. | |
1839 // It matches IE's behavior. | |
1840 if (base::win::IsTSFAwareRequired()) { | |
1841 ui::TSFBridge::GetInstance()->CancelComposition(); | |
1842 } else { | |
1843 imm32_manager_->CleanupComposition(m_hWnd); | |
1844 } | |
1845 // Fall through. | |
1846 case WM_MOUSEMOVE: | |
1847 case WM_MOUSELEAVE: { | |
1848 // Give the WebContentsImpl first crack at the message. It may want to | |
1849 // prevent forwarding to the renderer if some higher level browser | |
1850 // functionality is invoked. | |
1851 LPARAM parent_msg_lparam = lparam; | |
1852 if (message != WM_MOUSELEAVE) { | |
1853 // For the messages except WM_MOUSELEAVE, before forwarding them to | |
1854 // parent window, we should adjust cursor position from client | |
1855 // coordinates in current window to client coordinates in its parent | |
1856 // window. | |
1857 CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); | |
1858 ClientToScreen(&cursor_pos); | |
1859 GetParent().ScreenToClient(&cursor_pos); | |
1860 parent_msg_lparam = MAKELPARAM(cursor_pos.x, cursor_pos.y); | |
1861 } | |
1862 if (SendMessage(GetParent(), message, wparam, parent_msg_lparam) != 0) { | |
1863 TRACE_EVENT0("browser", "EarlyOut_SentToParent"); | |
1864 return 1; | |
1865 } | |
1866 } | |
1867 } | |
1868 } | |
1869 | |
1870 if (message == WM_LBUTTONDOWN && pointer_down_context_ && | |
1871 GetBrowserAccessibilityManager()) { | |
1872 GetBrowserAccessibilityManager()->GotMouseDown(); | |
1873 } | |
1874 | |
1875 if (message == WM_LBUTTONUP && ui::IsMouseEventFromTouch(message) && | |
1876 base::win::IsMetroProcess()) | |
1877 pointer_down_context_ = false; | |
1878 | |
1879 ForwardMouseEventToRenderer(message, wparam, lparam); | |
1880 return 0; | |
1881 } | |
1882 | |
1883 LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam, | |
1884 LPARAM lparam, BOOL& handled) { | |
1885 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnKeyEvent"); | |
1886 handled = TRUE; | |
1887 | |
1888 // When Escape is pressed, force fullscreen windows to close if necessary. | |
1889 if ((message == WM_KEYDOWN || message == WM_KEYUP) && wparam == VK_ESCAPE) { | |
1890 if (is_fullscreen_) { | |
1891 SendMessage(WM_CANCELMODE); | |
1892 return 0; | |
1893 } | |
1894 } | |
1895 | |
1896 // If we are a pop-up, forward tab related messages to our parent HWND, so | |
1897 // that we are dismissed appropriately and so that the focus advance in our | |
1898 // parent. | |
1899 // TODO(jcampan): http://b/issue?id=1192881 Could be abstracted in the | |
1900 // FocusManager. | |
1901 if (close_on_deactivate_ && | |
1902 (((message == WM_KEYDOWN || message == WM_KEYUP) && (wparam == VK_TAB)) || | |
1903 (message == WM_CHAR && wparam == L'\t'))) { | |
1904 // First close the pop-up. | |
1905 SendMessage(WM_CANCELMODE); | |
1906 // Then move the focus by forwarding the tab key to the parent. | |
1907 return ::SendMessage(GetParent(), message, wparam, lparam); | |
1908 } | |
1909 | |
1910 if (!render_widget_host_) | |
1911 return 0; | |
1912 | |
1913 // Bug 1845: we need to update the text direction when a user releases | |
1914 // either a right-shift key or a right-control key after pressing both of | |
1915 // them. So, we just update the text direction while a user is pressing the | |
1916 // keys, and we notify the text direction when a user releases either of them. | |
1917 // Bug 9718: http://crbug.com/9718 To investigate IE and notepad, this | |
1918 // shortcut is enabled only on a PC having RTL keyboard layouts installed. | |
1919 // We should emulate them. | |
1920 if (ui::IMM32Manager::IsRTLKeyboardLayoutInstalled()) { | |
1921 if (message == WM_KEYDOWN) { | |
1922 if (wparam == VK_SHIFT) { | |
1923 base::i18n::TextDirection dir; | |
1924 if (ui::IMM32Manager::IsCtrlShiftPressed(&dir)) { | |
1925 render_widget_host_->UpdateTextDirection( | |
1926 dir == base::i18n::RIGHT_TO_LEFT ? | |
1927 blink::WebTextDirectionRightToLeft : | |
1928 blink::WebTextDirectionLeftToRight); | |
1929 } | |
1930 } else if (wparam != VK_CONTROL) { | |
1931 // Bug 9762: http://crbug.com/9762 A user pressed a key except shift | |
1932 // and control keys. | |
1933 // When a user presses a key while he/she holds control and shift keys, | |
1934 // we cancel sending an IPC message in NotifyTextDirection() below and | |
1935 // ignore succeeding UpdateTextDirection() calls while we call | |
1936 // NotifyTextDirection(). | |
1937 // To cancel it, this call set a flag that prevents sending an IPC | |
1938 // message in NotifyTextDirection() only if we are going to send it. | |
1939 // It is harmless to call this function if we aren't going to send it. | |
1940 render_widget_host_->CancelUpdateTextDirection(); | |
1941 } | |
1942 } else if (message == WM_KEYUP && | |
1943 (wparam == VK_SHIFT || wparam == VK_CONTROL)) { | |
1944 // We send an IPC message only if we need to update the text direction. | |
1945 render_widget_host_->NotifyTextDirection(); | |
1946 } | |
1947 } | |
1948 | |
1949 // Special processing for enter key: When user hits enter in omnibox | |
1950 // we change focus to render host after the navigation, so repeat WM_KEYDOWNs | |
1951 // and WM_KEYUP are going to render host, despite being initiated in other | |
1952 // window. This code filters out these messages. | |
1953 bool ignore_keyboard_event = false; | |
1954 if (wparam == VK_RETURN) { | |
1955 if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN) { | |
1956 if (KF_REPEAT & HIWORD(lparam)) { | |
1957 // this is a repeated key | |
1958 if (!capture_enter_key_) | |
1959 ignore_keyboard_event = true; | |
1960 } else { | |
1961 capture_enter_key_ = true; | |
1962 } | |
1963 } else if (message == WM_KEYUP || message == WM_SYSKEYUP) { | |
1964 if (!capture_enter_key_) | |
1965 ignore_keyboard_event = true; | |
1966 capture_enter_key_ = false; | |
1967 } else { | |
1968 // Ignore all other keyboard events for the enter key if not captured. | |
1969 if (!capture_enter_key_) | |
1970 ignore_keyboard_event = true; | |
1971 } | |
1972 } | |
1973 | |
1974 if (render_widget_host_ && !ignore_keyboard_event) { | |
1975 MSG msg = { m_hWnd, message, wparam, lparam }; | |
1976 render_widget_host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(msg)); | |
1977 } | |
1978 | |
1979 return 0; | |
1980 } | |
1981 | |
1982 LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, | |
1983 LPARAM lparam, BOOL& handled) { | |
1984 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnWheelEvent"); | |
1985 // Forward the mouse-wheel message to the window under the mouse if it belongs | |
1986 // to us. | |
1987 if (message == WM_MOUSEWHEEL && | |
1988 ui::RerouteMouseWheel(m_hWnd, wparam, lparam)) { | |
1989 handled = TRUE; | |
1990 return 0; | |
1991 } | |
1992 | |
1993 // We get mouse wheel/scroll messages even if we are not in the foreground. | |
1994 // So here we check if we have any owned popup windows in the foreground and | |
1995 // dismiss them. | |
1996 if (m_hWnd != GetForegroundWindow()) { | |
1997 HWND toplevel_hwnd = ::GetAncestor(m_hWnd, GA_ROOT); | |
1998 EnumThreadWindows( | |
1999 GetCurrentThreadId(), | |
2000 DismissOwnedPopups, | |
2001 reinterpret_cast<LPARAM>(toplevel_hwnd)); | |
2002 } | |
2003 | |
2004 if (render_widget_host_) { | |
2005 blink::WebMouseWheelEvent wheel_event = | |
2006 WebMouseWheelEventBuilder::Build(m_hWnd, message, wparam, lparam); | |
2007 float scale = gfx::win::GetDeviceScaleFactor(); | |
2008 wheel_event.x /= scale; | |
2009 wheel_event.y /= scale; | |
2010 wheel_event.deltaX /= scale; | |
2011 wheel_event.deltaY /= scale; | |
2012 | |
2013 render_widget_host_->ForwardWheelEvent(wheel_event); | |
2014 } | |
2015 handled = TRUE; | |
2016 return 0; | |
2017 } | |
2018 | |
2019 WebTouchState::WebTouchState(const RenderWidgetHostViewWin* window) | |
2020 : window_(window), | |
2021 id_generator_(0) { | |
2022 } | |
2023 | |
2024 size_t WebTouchState::UpdateTouchPoints( | |
2025 TOUCHINPUT* points, size_t count) { | |
2026 // First we reset all touch event state. This involves removing any released | |
2027 // touchpoints and marking the rest as stationary. After that we go through | |
2028 // and alter/add any touchpoints (from the touch input buffer) that we can | |
2029 // coalesce into a single message. The return value is the number of consumed | |
2030 // input message. | |
2031 blink::WebTouchPoint* point = touch_event_.touches; | |
2032 blink::WebTouchPoint* end = point + touch_event_.touchesLength; | |
2033 while (point < end) { | |
2034 if (point->state == blink::WebTouchPoint::StateReleased) { | |
2035 *point = *(--end); | |
2036 --touch_event_.touchesLength; | |
2037 } else { | |
2038 point->state = blink::WebTouchPoint::StateStationary; | |
2039 point++; | |
2040 } | |
2041 } | |
2042 touch_event_.changedTouchesLength = 0; | |
2043 touch_event_.modifiers = content::EventFlagsToWebEventModifiers( | |
2044 ui::GetModifiersFromKeyState()); | |
2045 | |
2046 // Consume all events of the same type and add them to the changed list. | |
2047 int last_type = 0; | |
2048 for (size_t i = 0; i < count; ++i) { | |
2049 unsigned int mapped_id = GetMappedTouch(points[i].dwID); | |
2050 | |
2051 blink::WebTouchPoint* point = NULL; | |
2052 for (unsigned j = 0; j < touch_event_.touchesLength; ++j) { | |
2053 if (static_cast<DWORD>(touch_event_.touches[j].id) == mapped_id) { | |
2054 point = &touch_event_.touches[j]; | |
2055 break; | |
2056 } | |
2057 } | |
2058 | |
2059 // Use a move instead if we see a down on a point we already have. | |
2060 int type = GetTouchType(points[i]); | |
2061 if (point && type == TOUCHEVENTF_DOWN) | |
2062 SetTouchType(&points[i], TOUCHEVENTF_MOVE); | |
2063 | |
2064 // Stop processing when the event type changes. | |
2065 if (touch_event_.changedTouchesLength && type != last_type) | |
2066 return i; | |
2067 | |
2068 touch_event_.timeStampSeconds = points[i].dwTime / 1000.0; | |
2069 | |
2070 last_type = type; | |
2071 switch (type) { | |
2072 case TOUCHEVENTF_DOWN: { | |
2073 if (!(point = AddTouchPoint(&points[i]))) | |
2074 continue; | |
2075 touch_event_.type = blink::WebInputEvent::TouchStart; | |
2076 break; | |
2077 } | |
2078 | |
2079 case TOUCHEVENTF_UP: { | |
2080 if (!point) // Just throw away a stray up. | |
2081 continue; | |
2082 point->state = blink::WebTouchPoint::StateReleased; | |
2083 UpdateTouchPoint(point, &points[i]); | |
2084 touch_event_.type = blink::WebInputEvent::TouchEnd; | |
2085 break; | |
2086 } | |
2087 | |
2088 case TOUCHEVENTF_MOVE: { | |
2089 if (point) { | |
2090 point->state = blink::WebTouchPoint::StateMoved; | |
2091 // Don't update the message if the point didn't really move. | |
2092 if (UpdateTouchPoint(point, &points[i])) | |
2093 continue; | |
2094 touch_event_.type = blink::WebInputEvent::TouchMove; | |
2095 } else if (touch_event_.changedTouchesLength) { | |
2096 RemoveExpiredMappings(); | |
2097 // Can't add a point if we're already handling move events. | |
2098 return i; | |
2099 } else { | |
2100 // Treat a move with no existing point as a down. | |
2101 if (!(point = AddTouchPoint(&points[i]))) | |
2102 continue; | |
2103 last_type = TOUCHEVENTF_DOWN; | |
2104 SetTouchType(&points[i], TOUCHEVENTF_DOWN); | |
2105 touch_event_.type = blink::WebInputEvent::TouchStart; | |
2106 } | |
2107 break; | |
2108 } | |
2109 | |
2110 default: | |
2111 NOTREACHED(); | |
2112 continue; | |
2113 } | |
2114 touch_event_.changedTouches[touch_event_.changedTouchesLength++] = *point; | |
2115 } | |
2116 | |
2117 RemoveExpiredMappings(); | |
2118 return count; | |
2119 } | |
2120 | |
2121 void WebTouchState::RemoveExpiredMappings() { | |
2122 blink::WebTouchPoint* point = touch_event_.touches; | |
2123 blink::WebTouchPoint* end = point + touch_event_.touchesLength; | |
2124 for (; point < end; ++point) { | |
2125 if (point->state == blink::WebTouchPoint::StateReleased) | |
2126 id_generator_.ReleaseGeneratedID(point->id); | |
2127 } | |
2128 } | |
2129 | |
2130 | |
2131 bool WebTouchState::ReleaseTouchPoints() { | |
2132 if (touch_event_.touchesLength == 0) | |
2133 return false; | |
2134 // Mark every active touchpoint as released. | |
2135 touch_event_.type = blink::WebInputEvent::TouchEnd; | |
2136 touch_event_.changedTouchesLength = touch_event_.touchesLength; | |
2137 for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) { | |
2138 touch_event_.touches[i].state = blink::WebTouchPoint::StateReleased; | |
2139 touch_event_.changedTouches[i].state = | |
2140 blink::WebTouchPoint::StateReleased; | |
2141 } | |
2142 | |
2143 return true; | |
2144 } | |
2145 | |
2146 blink::WebTouchPoint* WebTouchState::AddTouchPoint( | |
2147 TOUCHINPUT* touch_input) { | |
2148 DCHECK(touch_event_.touchesLength < | |
2149 blink::WebTouchEvent::touchesLengthCap); | |
2150 if (touch_event_.touchesLength >= | |
2151 blink::WebTouchEvent::touchesLengthCap) | |
2152 return NULL; | |
2153 blink::WebTouchPoint* point = | |
2154 &touch_event_.touches[touch_event_.touchesLength++]; | |
2155 point->state = blink::WebTouchPoint::StatePressed; | |
2156 point->id = GetMappedTouch(touch_input->dwID); | |
2157 UpdateTouchPoint(point, touch_input); | |
2158 return point; | |
2159 } | |
2160 | |
2161 bool WebTouchState::UpdateTouchPoint( | |
2162 blink::WebTouchPoint* touch_point, | |
2163 TOUCHINPUT* touch_input) { | |
2164 CPoint coordinates( | |
2165 TOUCH_COORD_TO_PIXEL(touch_input->x) / | |
2166 gfx::win::GetUndocumentedDPITouchScale(), | |
2167 TOUCH_COORD_TO_PIXEL(touch_input->y) / | |
2168 gfx::win::GetUndocumentedDPITouchScale()); | |
2169 int radius_x = 1; | |
2170 int radius_y = 1; | |
2171 if (touch_input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) { | |
2172 // Some touch drivers send a contact area of "-1", yet flag it as valid. | |
2173 radius_x = std::max(1, | |
2174 static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cxContact) / | |
2175 gfx::win::GetUndocumentedDPITouchScale())); | |
2176 radius_y = std::max(1, | |
2177 static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cyContact) / | |
2178 gfx::win::GetUndocumentedDPITouchScale())); | |
2179 } | |
2180 | |
2181 // Detect and exclude stationary moves. | |
2182 if (GetTouchType(*touch_input) == TOUCHEVENTF_MOVE && | |
2183 touch_point->screenPosition.x == coordinates.x && | |
2184 touch_point->screenPosition.y == coordinates.y && | |
2185 touch_point->radiusX == radius_x && | |
2186 touch_point->radiusY == radius_y) { | |
2187 touch_point->state = blink::WebTouchPoint::StateStationary; | |
2188 return true; | |
2189 } | |
2190 | |
2191 touch_point->screenPosition.x = coordinates.x; | |
2192 touch_point->screenPosition.y = coordinates.y; | |
2193 window_->ScreenToClient(&coordinates); | |
2194 static float scale = gfx::win::GetDeviceScaleFactor(); | |
2195 touch_point->position.x = coordinates.x / scale; | |
2196 touch_point->position.y = coordinates.y / scale; | |
2197 touch_point->radiusX = radius_x; | |
2198 touch_point->radiusY = radius_y; | |
2199 touch_point->force = 0; | |
2200 touch_point->rotationAngle = 0; | |
2201 return false; | |
2202 } | |
2203 | |
2204 // Find (or create) a mapping for _os_touch_id_. | |
2205 unsigned int WebTouchState::GetMappedTouch(unsigned int os_touch_id) { | |
2206 return id_generator_.GetGeneratedID(os_touch_id); | |
2207 } | |
2208 | |
2209 LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam, | |
2210 LPARAM lparam, BOOL& handled) { | |
2211 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnTouchEvent"); | |
2212 // Finish the ongoing composition whenever a touch event happens. | |
2213 // It matches IE's behavior. | |
2214 if (base::win::IsTSFAwareRequired()) { | |
2215 ui::TSFBridge::GetInstance()->CancelComposition(); | |
2216 } else { | |
2217 imm32_manager_->CleanupComposition(m_hWnd); | |
2218 } | |
2219 | |
2220 // TODO(jschuh): Add support for an arbitrary number of touchpoints. | |
2221 size_t total = std::min(static_cast<int>(LOWORD(wparam)), | |
2222 static_cast<int>(blink::WebTouchEvent::touchesLengthCap)); | |
2223 TOUCHINPUT points[blink::WebTouchEvent::touchesLengthCap]; | |
2224 | |
2225 if (!total || !ui::GetTouchInputInfoWrapper((HTOUCHINPUT)lparam, total, | |
2226 points, sizeof(TOUCHINPUT))) { | |
2227 TRACE_EVENT0("browser", "EarlyOut_NothingToDo"); | |
2228 return 0; | |
2229 } | |
2230 | |
2231 if (total == 1 && (points[0].dwFlags & TOUCHEVENTF_DOWN)) { | |
2232 pointer_down_context_ = true; | |
2233 last_touch_location_ = gfx::Point( | |
2234 TOUCH_COORD_TO_PIXEL(points[0].x) / | |
2235 gfx::win::GetUndocumentedDPITouchScale(), | |
2236 TOUCH_COORD_TO_PIXEL(points[0].y) / | |
2237 gfx::win::GetUndocumentedDPITouchScale()); | |
2238 } | |
2239 | |
2240 bool should_forward = render_widget_host_->ShouldForwardTouchEvent() && | |
2241 touch_events_enabled_; | |
2242 | |
2243 // Send a copy of the touch events on to the gesture recognizer. | |
2244 for (size_t start = 0; start < total;) { | |
2245 start += touch_state_->UpdateTouchPoints(points + start, total - start); | |
2246 if (should_forward) { | |
2247 if (touch_state_->is_changed()) | |
2248 render_widget_host_->ForwardTouchEventWithLatencyInfo( | |
2249 touch_state_->touch_event(), ui::LatencyInfo()); | |
2250 } else { | |
2251 const blink::WebTouchEvent& touch_event = touch_state_->touch_event(); | |
2252 base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds( | |
2253 touch_event.timeStampSeconds * 1000); | |
2254 for (size_t i = 0; i < touch_event.touchesLength; ++i) { | |
2255 scoped_ptr<ui::GestureRecognizer::Gestures> gestures; | |
2256 gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture( | |
2257 TouchEventFromWebTouchPoint(touch_event.touches[i], timestamp), | |
2258 ui::ER_UNHANDLED, this)); | |
2259 ProcessGestures(gestures.get()); | |
2260 } | |
2261 } | |
2262 } | |
2263 | |
2264 CloseTouchInputHandle((HTOUCHINPUT)lparam); | |
2265 | |
2266 return 0; | |
2267 } | |
2268 | |
2269 void RenderWidgetHostViewWin::ProcessGestures( | |
2270 ui::GestureRecognizer::Gestures* gestures) { | |
2271 if ((gestures == NULL) || gestures->empty()) | |
2272 return; | |
2273 for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin(); | |
2274 g_it != gestures->end(); | |
2275 ++g_it) { | |
2276 ForwardGestureEventToRenderer(*g_it); | |
2277 } | |
2278 } | |
2279 | |
2280 LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message, | |
2281 WPARAM wparam, | |
2282 LPARAM lparam, | |
2283 BOOL& handled) { | |
2284 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnMouseActivate"); | |
2285 if (!render_widget_host_) | |
2286 return MA_NOACTIVATE; | |
2287 | |
2288 if (!IsActivatable()) | |
2289 return MA_NOACTIVATE; | |
2290 | |
2291 HWND focus_window = GetFocus(); | |
2292 if (!::IsWindow(focus_window) || !IsChild(focus_window)) { | |
2293 // We handle WM_MOUSEACTIVATE to set focus to the underlying plugin | |
2294 // child window. This is to ensure that keyboard events are received | |
2295 // by the plugin. The correct way to fix this would be send over | |
2296 // an event to the renderer which would then eventually send over | |
2297 // a setFocus call to the plugin widget. This would ensure that | |
2298 // the renderer (webkit) knows about the plugin widget receiving | |
2299 // focus. | |
2300 // TODO(iyengar) Do the right thing as per the above comment. | |
2301 POINT cursor_pos = {0}; | |
2302 ::GetCursorPos(&cursor_pos); | |
2303 ::ScreenToClient(m_hWnd, &cursor_pos); | |
2304 HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); | |
2305 if (::IsWindow(child_window) && child_window != m_hWnd) { | |
2306 if (gfx::GetClassName(child_window) == kWrapperNativeWindowClassName) | |
2307 child_window = ::GetWindow(child_window, GW_CHILD); | |
2308 | |
2309 ::SetFocus(child_window); | |
2310 return MA_NOACTIVATE; | |
2311 } | |
2312 } | |
2313 handled = FALSE; | |
2314 render_widget_host_->OnPointerEventActivate(); | |
2315 return MA_ACTIVATE; | |
2316 } | |
2317 | |
2318 LRESULT RenderWidgetHostViewWin::OnGestureEvent( | |
2319 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
2320 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGestureEvent"); | |
2321 | |
2322 DCHECK(!touch_events_enabled_); | |
2323 handled = FALSE; | |
2324 | |
2325 GESTUREINFO gi = {sizeof(GESTUREINFO)}; | |
2326 HGESTUREINFO gi_handle = reinterpret_cast<HGESTUREINFO>(lparam); | |
2327 if (!::GetGestureInfo(gi_handle, &gi)) { | |
2328 DWORD error = GetLastError(); | |
2329 NOTREACHED() << "Unable to get gesture info. Error : " << error; | |
2330 return 0; | |
2331 } | |
2332 | |
2333 if (gi.dwID == GID_ZOOM) { | |
2334 PageZoom zoom = PAGE_ZOOM_RESET; | |
2335 POINT zoom_center = {0}; | |
2336 if (DecodeZoomGesture(m_hWnd, gi, &zoom, &zoom_center)) { | |
2337 handled = TRUE; | |
2338 Send(new ViewMsg_ZoomFactor(render_widget_host_->GetRoutingID(), | |
2339 zoom, zoom_center.x, zoom_center.y)); | |
2340 } | |
2341 } else if (gi.dwID == GID_PAN) { | |
2342 // Right now we only decode scroll gestures and we forward to the page | |
2343 // as scroll events. | |
2344 POINT start; | |
2345 POINT delta; | |
2346 if (DecodeScrollGesture(gi, &start, &delta)) { | |
2347 handled = TRUE; | |
2348 render_widget_host_->ForwardWheelEvent( | |
2349 MakeFakeScrollWheelEvent(m_hWnd, start, delta)); | |
2350 } | |
2351 } | |
2352 ::CloseGestureInfoHandle(gi_handle); | |
2353 return 0; | |
2354 } | |
2355 | |
2356 LRESULT RenderWidgetHostViewWin::OnMoveOrSize( | |
2357 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
2358 // Reset the cliping rectangle if the mouse is locked. | |
2359 if (mouse_locked_) { | |
2360 CRect rect; | |
2361 GetWindowRect(&rect); | |
2362 ::ClipCursor(&rect); | |
2363 } | |
2364 return 0; | |
2365 } | |
2366 | |
2367 void RenderWidgetHostViewWin::CreateBrowserAccessibilityManagerIfNeeded() { | |
2368 if (GetBrowserAccessibilityManager()) | |
2369 return; | |
2370 | |
2371 HRESULT hr = ::CreateStdAccessibleObject( | |
2372 m_hWnd, OBJID_WINDOW, IID_IAccessible, | |
2373 reinterpret_cast<void **>(&window_iaccessible_)); | |
2374 DCHECK(SUCCEEDED(hr)); | |
2375 | |
2376 SetBrowserAccessibilityManager( | |
2377 new BrowserAccessibilityManagerWin( | |
2378 m_hWnd, | |
2379 window_iaccessible_.get(), | |
2380 BrowserAccessibilityManagerWin::GetEmptyDocument(), | |
2381 this)); | |
2382 } | |
2383 | |
2384 bool RenderWidgetHostViewWin::LockMouse() { | |
2385 if (mouse_locked_) | |
2386 return true; | |
2387 | |
2388 mouse_locked_ = true; | |
2389 | |
2390 // Hide the tooltip window if it is currently visible. When the mouse is | |
2391 // locked, no mouse message is relayed to the tooltip window, so we don't need | |
2392 // to worry that it will reappear. | |
2393 if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) { | |
2394 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); | |
2395 // Sending a TTM_POP message doesn't seem to actually hide the tooltip | |
2396 // window, although we will receive a TTN_POP notification. As a result, | |
2397 // ShowWindow() is explicitly called to hide the window. | |
2398 ::ShowWindow(tooltip_hwnd_, SW_HIDE); | |
2399 } | |
2400 | |
2401 // TODO(yzshen): ShowCursor(FALSE) causes SetCursorPos() to be ignored on | |
2402 // Remote Desktop. | |
2403 ::ShowCursor(FALSE); | |
2404 | |
2405 move_to_center_request_.pending = false; | |
2406 last_mouse_position_.locked_global = last_mouse_position_.unlocked_global; | |
2407 | |
2408 // Must set the clip rectangle before MoveCursorToCenterIfNecessary() | |
2409 // so that if the cursor is moved it uses the clip rect set to the window | |
2410 // rect. Otherwise, MoveCursorToCenterIfNecessary() may move the cursor | |
2411 // to the center of the screen, and then we would clip to the window | |
2412 // rect, thus moving the cursor and causing a movement delta. | |
2413 CRect rect; | |
2414 GetWindowRect(&rect); | |
2415 ::ClipCursor(&rect); | |
2416 MoveCursorToCenterIfNecessary(); | |
2417 | |
2418 return true; | |
2419 } | |
2420 | |
2421 void RenderWidgetHostViewWin::UnlockMouse() { | |
2422 if (!mouse_locked_) | |
2423 return; | |
2424 | |
2425 mouse_locked_ = false; | |
2426 | |
2427 ::ClipCursor(NULL); | |
2428 ::SetCursorPos(last_mouse_position_.unlocked_global.x(), | |
2429 last_mouse_position_.unlocked_global.y()); | |
2430 ::ShowCursor(TRUE); | |
2431 | |
2432 if (render_widget_host_) | |
2433 render_widget_host_->LostMouseLock(); | |
2434 } | |
2435 | |
2436 void RenderWidgetHostViewWin::Observe( | |
2437 int type, | |
2438 const NotificationSource& source, | |
2439 const NotificationDetails& details) { | |
2440 DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED); | |
2441 | |
2442 // Get the RenderProcessHost that posted this notification, and exit | |
2443 // if it's not the one associated with this host view. | |
2444 RenderProcessHost* render_process_host = | |
2445 Source<RenderProcessHost>(source).ptr(); | |
2446 DCHECK(render_process_host); | |
2447 if (!render_widget_host_ || | |
2448 render_process_host != render_widget_host_->GetProcess()) { | |
2449 return; | |
2450 } | |
2451 | |
2452 // If it was our RenderProcessHost that posted the notification, | |
2453 // clear the BrowserAccessibilityManager, because the renderer is | |
2454 // dead and any accessibility information we have is now stale. | |
2455 SetBrowserAccessibilityManager(NULL); | |
2456 } | |
2457 | |
2458 static void PaintCompositorHostWindow(HWND hWnd) { | |
2459 PAINTSTRUCT paint; | |
2460 BeginPaint(hWnd, &paint); | |
2461 | |
2462 RenderWidgetHostViewWin* win = static_cast<RenderWidgetHostViewWin*>( | |
2463 gfx::GetWindowUserData(hWnd)); | |
2464 // Trigger composite to rerender window. | |
2465 if (win) | |
2466 win->AcceleratedPaint(paint.hdc); | |
2467 | |
2468 EndPaint(hWnd, &paint); | |
2469 } | |
2470 | |
2471 // WndProc for the compositor host window. We use this instead of Default so | |
2472 // we can drop WM_PAINT and WM_ERASEBKGD messages on the floor. | |
2473 static LRESULT CALLBACK CompositorHostWindowProc(HWND hWnd, UINT message, | |
2474 WPARAM wParam, LPARAM lParam) { | |
2475 switch (message) { | |
2476 case WM_ERASEBKGND: | |
2477 return 0; | |
2478 case WM_PAINT: | |
2479 PaintCompositorHostWindow(hWnd); | |
2480 return 0; | |
2481 default: | |
2482 return DefWindowProc(hWnd, message, wParam, lParam); | |
2483 } | |
2484 } | |
2485 | |
2486 void RenderWidgetHostViewWin::AcceleratedPaint(HDC dc) { | |
2487 if (render_widget_host_) | |
2488 render_widget_host_->ScheduleComposite(); | |
2489 if (accelerated_surface_) | |
2490 accelerated_surface_->Present(dc); | |
2491 } | |
2492 | |
2493 void RenderWidgetHostViewWin::GetScreenInfo(blink::WebScreenInfo* results) { | |
2494 GetScreenInfoForWindow(GetNativeViewId(), results); | |
2495 } | |
2496 | |
2497 gfx::Rect RenderWidgetHostViewWin::GetBoundsInRootWindow() { | |
2498 RECT window_rect = {0}; | |
2499 HWND root_window = GetAncestor(m_hWnd, GA_ROOT); | |
2500 ::GetWindowRect(root_window, &window_rect); | |
2501 gfx::Rect rect(window_rect); | |
2502 | |
2503 // Maximized windows are outdented from the work area by the frame thickness | |
2504 // even though this "frame" is not painted. This confuses code (and people) | |
2505 // that think of a maximized window as corresponding exactly to the work area. | |
2506 // Correct for this by subtracting the frame thickness back off. | |
2507 if (::IsZoomed(root_window)) { | |
2508 rect.Inset(GetSystemMetrics(SM_CXSIZEFRAME), | |
2509 GetSystemMetrics(SM_CYSIZEFRAME)); | |
2510 } | |
2511 | |
2512 return gfx::win::ScreenToDIPRect(rect); | |
2513 } | |
2514 | |
2515 // Creates a HWND within the RenderWidgetHostView that will serve as a host | |
2516 // for a HWND that the GPU process will create. The host window is used | |
2517 // to Z-position the GPU's window relative to other plugin windows. | |
2518 gfx::GLSurfaceHandle RenderWidgetHostViewWin::GetCompositingSurface() { | |
2519 // If the window has been created, don't recreate it a second time | |
2520 if (compositor_host_window_) | |
2521 return gfx::GLSurfaceHandle(compositor_host_window_, gfx::NATIVE_TRANSPORT); | |
2522 | |
2523 // On Vista and later we present directly to the view window rather than a | |
2524 // child window. | |
2525 if (GpuDataManagerImpl::GetInstance()->IsUsingAcceleratedSurface()) { | |
2526 if (!accelerated_surface_) | |
2527 accelerated_surface_.reset(new AcceleratedSurface(m_hWnd)); | |
2528 return gfx::GLSurfaceHandle(m_hWnd, gfx::NATIVE_TRANSPORT); | |
2529 } | |
2530 | |
2531 // On XP we need a child window that can be resized independently of the | |
2532 // parent. | |
2533 static ATOM atom = 0; | |
2534 static HMODULE instance = NULL; | |
2535 if (!atom) { | |
2536 WNDCLASSEX window_class; | |
2537 base::win::InitializeWindowClass( | |
2538 L"CompositorHostWindowClass", | |
2539 &base::win::WrappedWindowProc<CompositorHostWindowProc>, | |
2540 0, 0, 0, NULL, NULL, NULL, NULL, NULL, | |
2541 &window_class); | |
2542 instance = window_class.hInstance; | |
2543 atom = RegisterClassEx(&window_class); | |
2544 DCHECK(atom); | |
2545 } | |
2546 | |
2547 RECT currentRect; | |
2548 GetClientRect(¤tRect); | |
2549 | |
2550 // Ensure window does not have zero area because D3D cannot create a zero | |
2551 // area swap chain. | |
2552 int width = std::max(1, | |
2553 static_cast<int>(currentRect.right - currentRect.left)); | |
2554 int height = std::max(1, | |
2555 static_cast<int>(currentRect.bottom - currentRect.top)); | |
2556 | |
2557 compositor_host_window_ = CreateWindowEx( | |
2558 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, | |
2559 MAKEINTATOM(atom), 0, | |
2560 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED, | |
2561 0, 0, width, height, m_hWnd, 0, instance, 0); | |
2562 gfx::CheckWindowCreated(compositor_host_window_); | |
2563 | |
2564 gfx::SetWindowUserData(compositor_host_window_, this); | |
2565 | |
2566 gfx::GLSurfaceHandle surface_handle(compositor_host_window_, | |
2567 gfx::NATIVE_TRANSPORT); | |
2568 return surface_handle; | |
2569 } | |
2570 | |
2571 void RenderWidgetHostViewWin::ResizeCompositingSurface(const gfx::Size& size) { | |
2572 // Ensure window does not have zero area because D3D cannot create a zero | |
2573 // area swap chain. | |
2574 ::SetWindowPos(compositor_host_window_, | |
2575 NULL, | |
2576 0, 0, | |
2577 std::max(1, size.width()), | |
2578 std::max(1, size.height()), | |
2579 SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOZORDER | | |
2580 SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOMOVE); | |
2581 } | |
2582 | |
2583 void RenderWidgetHostViewWin::OnAcceleratedCompositingStateChange() { | |
2584 bool show = render_widget_host_->is_accelerated_compositing_active(); | |
2585 // When we first create the compositor, we will get a show request from | |
2586 // the renderer before we have gotten the create request from the GPU. In this | |
2587 // case, simply ignore the show request. | |
2588 if (compositor_host_window_ == NULL) | |
2589 return; | |
2590 | |
2591 if (show) { | |
2592 ::ShowWindow(compositor_host_window_, SW_SHOW); | |
2593 | |
2594 // Get all the child windows of this view, including the compositor window. | |
2595 std::vector<HWND> all_child_windows; | |
2596 ::EnumChildWindows(m_hWnd, AddChildWindowToVector, | |
2597 reinterpret_cast<LPARAM>(&all_child_windows)); | |
2598 | |
2599 // Build a list of just the plugin window handles | |
2600 std::vector<HWND> plugin_windows; | |
2601 bool compositor_host_window_found = false; | |
2602 for (size_t i = 0; i < all_child_windows.size(); ++i) { | |
2603 if (all_child_windows[i] != compositor_host_window_) | |
2604 plugin_windows.push_back(all_child_windows[i]); | |
2605 else | |
2606 compositor_host_window_found = true; | |
2607 } | |
2608 DCHECK(compositor_host_window_found); | |
2609 | |
2610 // Set all the plugin windows to be "after" the compositor window. | |
2611 // When the compositor window is created, gets placed above plugins. | |
2612 for (size_t i = 0; i < plugin_windows.size(); ++i) { | |
2613 HWND next; | |
2614 if (i + 1 < plugin_windows.size()) | |
2615 next = plugin_windows[i+1]; | |
2616 else | |
2617 next = compositor_host_window_; | |
2618 ::SetWindowPos(plugin_windows[i], next, 0, 0, 0, 0, | |
2619 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); | |
2620 } | |
2621 } else { | |
2622 // Drop the backing store for the accelerated surface when the accelerated | |
2623 // compositor is disabled. Otherwise, a flash of the last presented frame | |
2624 // could appear when it is next enabled. | |
2625 if (accelerated_surface_) | |
2626 accelerated_surface_->Suspend(); | |
2627 hide_compositor_window_at_next_paint_ = true; | |
2628 } | |
2629 } | |
2630 | |
2631 void RenderWidgetHostViewWin::AcceleratedSurfaceInitialized(int host_id, | |
2632 int route_id) { | |
2633 } | |
2634 | |
2635 void RenderWidgetHostViewWin::AcceleratedSurfaceBuffersSwapped( | |
2636 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, | |
2637 int gpu_host_id) { | |
2638 NOTREACHED(); | |
2639 } | |
2640 | |
2641 void RenderWidgetHostViewWin::AcceleratedSurfacePostSubBuffer( | |
2642 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, | |
2643 int gpu_host_id) { | |
2644 NOTREACHED(); | |
2645 } | |
2646 | |
2647 void RenderWidgetHostViewWin::AcceleratedSurfaceSuspend() { | |
2648 if (!accelerated_surface_) | |
2649 return; | |
2650 | |
2651 accelerated_surface_->Suspend(); | |
2652 } | |
2653 | |
2654 void RenderWidgetHostViewWin::AcceleratedSurfaceRelease() { | |
2655 } | |
2656 | |
2657 bool RenderWidgetHostViewWin::HasAcceleratedSurface( | |
2658 const gfx::Size& desired_size) { | |
2659 // TODO(jbates) Implement this so this view can use GetBackingStore for both | |
2660 // software and GPU frames. Defaulting to false just makes GetBackingStore | |
2661 // only useable for software frames. | |
2662 return false; | |
2663 } | |
2664 | |
2665 void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) { | |
2666 if (!render_widget_host_) | |
2667 return; | |
2668 | |
2669 render_widget_host_->AccessibilitySetFocus(acc_obj_id); | |
2670 } | |
2671 | |
2672 void RenderWidgetHostViewWin::AccessibilityDoDefaultAction(int acc_obj_id) { | |
2673 if (!render_widget_host_) | |
2674 return; | |
2675 | |
2676 render_widget_host_->AccessibilityDoDefaultAction(acc_obj_id); | |
2677 } | |
2678 | |
2679 void RenderWidgetHostViewWin::AccessibilityScrollToMakeVisible( | |
2680 int acc_obj_id, gfx::Rect subfocus) { | |
2681 if (!render_widget_host_) | |
2682 return; | |
2683 | |
2684 render_widget_host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus); | |
2685 } | |
2686 | |
2687 void RenderWidgetHostViewWin::AccessibilityScrollToPoint( | |
2688 int acc_obj_id, gfx::Point point) { | |
2689 if (!render_widget_host_) | |
2690 return; | |
2691 | |
2692 render_widget_host_->AccessibilityScrollToPoint(acc_obj_id, point); | |
2693 } | |
2694 | |
2695 void RenderWidgetHostViewWin::AccessibilitySetTextSelection( | |
2696 int acc_obj_id, int start_offset, int end_offset) { | |
2697 if (!render_widget_host_) | |
2698 return; | |
2699 | |
2700 render_widget_host_->AccessibilitySetTextSelection( | |
2701 acc_obj_id, start_offset, end_offset); | |
2702 } | |
2703 | |
2704 gfx::Point RenderWidgetHostViewWin::GetLastTouchEventLocation() const { | |
2705 return last_touch_location_; | |
2706 } | |
2707 | |
2708 void RenderWidgetHostViewWin::FatalAccessibilityTreeError() { | |
2709 render_widget_host_->FatalAccessibilityTreeError(); | |
2710 SetBrowserAccessibilityManager(NULL); | |
2711 } | |
2712 | |
2713 LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam, | |
2714 LPARAM lparam, BOOL& handled) { | |
2715 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGetObject"); | |
2716 if (kIdCustom == lparam) { | |
2717 // An MSAA client requestes our custom id. Assume that we have detected an | |
2718 // active windows screen reader. | |
2719 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected(); | |
2720 render_widget_host_->SetAccessibilityMode( | |
2721 BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()); | |
2722 | |
2723 // Return with failure. | |
2724 return static_cast<LRESULT>(0L); | |
2725 } | |
2726 | |
2727 if (lparam != OBJID_CLIENT) { | |
2728 handled = false; | |
2729 return static_cast<LRESULT>(0L); | |
2730 } | |
2731 | |
2732 IAccessible* iaccessible = GetNativeViewAccessible(); | |
2733 if (iaccessible) | |
2734 return LresultFromObject(IID_IAccessible, wparam, iaccessible); | |
2735 | |
2736 handled = false; | |
2737 return static_cast<LRESULT>(0L); | |
2738 } | |
2739 | |
2740 LRESULT RenderWidgetHostViewWin::OnParentNotify(UINT message, WPARAM wparam, | |
2741 LPARAM lparam, BOOL& handled) { | |
2742 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnParentNotify"); | |
2743 handled = FALSE; | |
2744 | |
2745 if (!render_widget_host_) | |
2746 return 0; | |
2747 | |
2748 switch (LOWORD(wparam)) { | |
2749 case WM_LBUTTONDOWN: | |
2750 case WM_RBUTTONDOWN: | |
2751 case WM_MBUTTONDOWN: | |
2752 render_widget_host_->StartUserGesture(); | |
2753 break; | |
2754 default: | |
2755 break; | |
2756 } | |
2757 return 0; | |
2758 } | |
2759 | |
2760 void RenderWidgetHostViewWin::OnFinalMessage(HWND window) { | |
2761 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnFinalMessage"); | |
2762 // When the render widget host is being destroyed, it ends up calling | |
2763 // Destroy() which NULLs render_widget_host_. | |
2764 // Note: the following bug http://crbug.com/24248 seems to report that | |
2765 // OnFinalMessage is called with a deleted |render_widget_host_|. It is not | |
2766 // clear how this could happen, hence the NULLing of render_widget_host_ | |
2767 // above. | |
2768 if (!render_widget_host_ && !being_destroyed_) { | |
2769 // If you hit this NOTREACHED, please add a comment to report it on | |
2770 // http://crbug.com/24248, including what you did when it happened and if | |
2771 // you can repro. | |
2772 NOTREACHED(); | |
2773 } | |
2774 if (render_widget_host_) | |
2775 render_widget_host_->ViewDestroyed(); | |
2776 if (base::win::IsTSFAwareRequired()) | |
2777 ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); | |
2778 delete this; | |
2779 } | |
2780 | |
2781 LRESULT RenderWidgetHostViewWin::OnSessionChange(UINT message, | |
2782 WPARAM wparam, | |
2783 LPARAM lparam, | |
2784 BOOL& handled) { | |
2785 handled = FALSE; | |
2786 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnSessionChange"); | |
2787 | |
2788 if (!accelerated_surface_) | |
2789 return 0; | |
2790 | |
2791 switch (wparam) { | |
2792 case WTS_SESSION_LOCK: | |
2793 accelerated_surface_->SetIsSessionLocked(true); | |
2794 break; | |
2795 case WTS_SESSION_UNLOCK: | |
2796 // Force a repaint to update the window contents. | |
2797 if (!render_widget_host_->is_hidden()) | |
2798 InvalidateRect(NULL, FALSE); | |
2799 accelerated_surface_->SetIsSessionLocked(false); | |
2800 break; | |
2801 default: | |
2802 break; | |
2803 } | |
2804 | |
2805 return 0; | |
2806 } | |
2807 | |
2808 void RenderWidgetHostViewWin::TrackMouseLeave(bool track) { | |
2809 if (track == track_mouse_leave_) | |
2810 return; | |
2811 track_mouse_leave_ = track; | |
2812 | |
2813 DCHECK(m_hWnd); | |
2814 | |
2815 TRACKMOUSEEVENT tme; | |
2816 tme.cbSize = sizeof(TRACKMOUSEEVENT); | |
2817 tme.dwFlags = TME_LEAVE; | |
2818 if (!track_mouse_leave_) | |
2819 tme.dwFlags |= TME_CANCEL; | |
2820 tme.hwndTrack = m_hWnd; | |
2821 | |
2822 TrackMouseEvent(&tme); | |
2823 } | |
2824 | |
2825 bool RenderWidgetHostViewWin::Send(IPC::Message* message) { | |
2826 if (!render_widget_host_) | |
2827 return false; | |
2828 return render_widget_host_->Send(message); | |
2829 } | |
2830 | |
2831 void RenderWidgetHostViewWin::EnsureTooltip() { | |
2832 UINT message = TTM_NEWTOOLRECT; | |
2833 | |
2834 TOOLINFO ti = {0}; | |
2835 ti.cbSize = sizeof(ti); | |
2836 ti.hwnd = m_hWnd; | |
2837 ti.uId = 0; | |
2838 if (!::IsWindow(tooltip_hwnd_)) { | |
2839 message = TTM_ADDTOOL; | |
2840 tooltip_hwnd_ = CreateWindowEx( | |
2841 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), | |
2842 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, m_hWnd, NULL, | |
2843 NULL, NULL); | |
2844 if (!tooltip_hwnd_) { | |
2845 // Tooltip creation can inexplicably fail. See bug 82913 for details. | |
2846 LOG_GETLASTERROR(WARNING) << | |
2847 "Tooltip creation failed, tooltips won't work"; | |
2848 return; | |
2849 } | |
2850 ti.uFlags = TTF_TRANSPARENT; | |
2851 ti.lpszText = LPSTR_TEXTCALLBACK; | |
2852 | |
2853 // Ensure web content tooltips are displayed for at least this amount of | |
2854 // time, to give users a chance to read longer messages. | |
2855 const int kMinimumAutopopDurationMs = 10 * 1000; | |
2856 int autopop_duration_ms = | |
2857 SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_AUTOPOP, NULL); | |
2858 if (autopop_duration_ms < kMinimumAutopopDurationMs) { | |
2859 SendMessage(tooltip_hwnd_, TTM_SETDELAYTIME, TTDT_AUTOPOP, | |
2860 kMinimumAutopopDurationMs); | |
2861 } | |
2862 } | |
2863 | |
2864 CRect cr; | |
2865 GetClientRect(&ti.rect); | |
2866 SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti)); | |
2867 } | |
2868 | |
2869 void RenderWidgetHostViewWin::ResetTooltip() { | |
2870 if (::IsWindow(tooltip_hwnd_)) | |
2871 ::DestroyWindow(tooltip_hwnd_); | |
2872 tooltip_hwnd_ = NULL; | |
2873 } | |
2874 | |
2875 bool RenderWidgetHostViewWin::ForwardGestureEventToRenderer( | |
2876 ui::GestureEvent* gesture) { | |
2877 if (!render_widget_host_) | |
2878 return false; | |
2879 | |
2880 // Pinch gestures are disabled by default on windows desktop. See | |
2881 // crbug.com/128477 and crbug.com/148816 | |
2882 if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN || | |
2883 gesture->type() == ui::ET_GESTURE_PINCH_UPDATE || | |
2884 gesture->type() == ui::ET_GESTURE_PINCH_END) && | |
2885 !ShouldSendPinchGesture()) { | |
2886 return true; | |
2887 } | |
2888 | |
2889 blink::WebGestureEvent web_gesture = CreateWebGestureEvent(m_hWnd, *gesture); | |
2890 if (web_gesture.type == blink::WebGestureEvent::Undefined) | |
2891 return false; | |
2892 if (web_gesture.type == blink::WebGestureEvent::GestureTapDown) { | |
2893 render_widget_host_->ForwardGestureEvent( | |
2894 CreateFlingCancelEvent(gesture->time_stamp().InSecondsF())); | |
2895 } | |
2896 render_widget_host_->ForwardGestureEventWithLatencyInfo(web_gesture, | |
2897 *gesture->latency()); | |
2898 return true; | |
2899 } | |
2900 | |
2901 void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, | |
2902 WPARAM wparam, | |
2903 LPARAM lparam) { | |
2904 TRACE_EVENT0("browser", | |
2905 "RenderWidgetHostViewWin::ForwardMouseEventToRenderer"); | |
2906 if (!render_widget_host_) { | |
2907 TRACE_EVENT0("browser", "EarlyOut_NoRWH"); | |
2908 return; | |
2909 } | |
2910 | |
2911 gfx::Point point = gfx::win::ScreenToDIPPoint( | |
2912 gfx::Point(static_cast<short>(LOWORD(lparam)), | |
2913 static_cast<short>(HIWORD(lparam)))); | |
2914 lparam = MAKELPARAM(point.x(), point.y()); | |
2915 | |
2916 WebMouseEvent event( | |
2917 WebMouseEventBuilder::Build(m_hWnd, message, wparam, lparam)); | |
2918 | |
2919 if (mouse_locked_) { | |
2920 event.movementX = event.globalX - last_mouse_position_.locked_global.x(); | |
2921 event.movementY = event.globalY - last_mouse_position_.locked_global.y(); | |
2922 last_mouse_position_.locked_global.SetPoint(event.globalX, event.globalY); | |
2923 | |
2924 event.x = last_mouse_position_.unlocked.x(); | |
2925 event.y = last_mouse_position_.unlocked.y(); | |
2926 event.windowX = last_mouse_position_.unlocked.x(); | |
2927 event.windowY = last_mouse_position_.unlocked.y(); | |
2928 event.globalX = last_mouse_position_.unlocked_global.x(); | |
2929 event.globalY = last_mouse_position_.unlocked_global.y(); | |
2930 } else { | |
2931 if (ignore_mouse_movement_) { | |
2932 ignore_mouse_movement_ = false; | |
2933 event.movementX = 0; | |
2934 event.movementY = 0; | |
2935 } else { | |
2936 event.movementX = | |
2937 event.globalX - last_mouse_position_.unlocked_global.x(); | |
2938 event.movementY = | |
2939 event.globalY - last_mouse_position_.unlocked_global.y(); | |
2940 } | |
2941 | |
2942 last_mouse_position_.unlocked.SetPoint(event.windowX, event.windowY); | |
2943 last_mouse_position_.unlocked_global.SetPoint(event.globalX, event.globalY); | |
2944 } | |
2945 | |
2946 // Windows sends (fake) mouse messages for touch events. Don't send these to | |
2947 // the render widget. | |
2948 if (!touch_events_enabled_ || !ui::IsMouseEventFromTouch(message)) { | |
2949 // Send the event to the renderer before changing mouse capture, so that | |
2950 // the capturelost event arrives after mouseup. | |
2951 render_widget_host_->ForwardMouseEvent(event); | |
2952 | |
2953 switch (event.type) { | |
2954 case WebInputEvent::MouseMove: | |
2955 TrackMouseLeave(true); | |
2956 break; | |
2957 case WebInputEvent::MouseLeave: | |
2958 TrackMouseLeave(false); | |
2959 break; | |
2960 case WebInputEvent::MouseDown: | |
2961 SetCapture(); | |
2962 break; | |
2963 case WebInputEvent::MouseUp: | |
2964 if (GetCapture() == m_hWnd) | |
2965 ReleaseCapture(); | |
2966 break; | |
2967 } | |
2968 } | |
2969 | |
2970 if (IsActivatable() && event.type == WebInputEvent::MouseDown) { | |
2971 // This is a temporary workaround for bug 765011 to get focus when the | |
2972 // mouse is clicked. This happens after the mouse down event is sent to | |
2973 // the renderer because normally Windows does a WM_SETFOCUS after | |
2974 // WM_LBUTTONDOWN. | |
2975 SetFocus(); | |
2976 } | |
2977 } | |
2978 | |
2979 void RenderWidgetHostViewWin::ShutdownHost() { | |
2980 weak_factory_.InvalidateWeakPtrs(); | |
2981 if (render_widget_host_) | |
2982 render_widget_host_->Shutdown(); | |
2983 // Do not touch any members at this point, |this| has been deleted. | |
2984 } | |
2985 | |
2986 void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd, | |
2987 const gfx::Rect& pos, | |
2988 DWORD ex_style) { | |
2989 Create(parent_hwnd, NULL, NULL, WS_POPUP, ex_style); | |
2990 gfx::Rect screen_rect = gfx::win::DIPToScreenRect(pos); | |
2991 MoveWindow(screen_rect.x(), screen_rect.y(), screen_rect.width(), | |
2992 screen_rect.height(), TRUE); | |
2993 ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA); | |
2994 | |
2995 if (is_fullscreen_ && win8::IsSingleWindowMetroMode()) { | |
2996 MetroSetFrameWindow set_frame_window = | |
2997 reinterpret_cast<MetroSetFrameWindow>( | |
2998 ::GetProcAddress(base::win::GetMetroModule(), "SetFrameWindow")); | |
2999 DCHECK(set_frame_window); | |
3000 set_frame_window(m_hWnd); | |
3001 } | |
3002 } | |
3003 | |
3004 CPoint RenderWidgetHostViewWin::GetClientCenter() const { | |
3005 CRect rect; | |
3006 GetClientRect(&rect); | |
3007 return rect.CenterPoint(); | |
3008 } | |
3009 | |
3010 void RenderWidgetHostViewWin::MoveCursorToCenterIfNecessary() { | |
3011 DCHECK(mouse_locked_); | |
3012 | |
3013 CRect rect; | |
3014 GetClipCursor(&rect); | |
3015 int border_x = rect.Width() * kMouseLockBorderPercentage / 100; | |
3016 int border_y = rect.Height() * kMouseLockBorderPercentage / 100; | |
3017 | |
3018 bool should_move = | |
3019 last_mouse_position_.locked_global.x() < rect.left + border_x || | |
3020 last_mouse_position_.locked_global.x() > rect.right - border_x || | |
3021 last_mouse_position_.locked_global.y() < rect.top + border_y || | |
3022 last_mouse_position_.locked_global.y() > rect.bottom - border_y; | |
3023 | |
3024 if (should_move) { | |
3025 move_to_center_request_.pending = true; | |
3026 move_to_center_request_.target = rect.CenterPoint(); | |
3027 if (!::SetCursorPos(move_to_center_request_.target.x(), | |
3028 move_to_center_request_.target.y())) { | |
3029 LOG_GETLASTERROR(WARNING) << "Failed to set cursor position."; | |
3030 } | |
3031 } | |
3032 } | |
3033 | |
3034 void RenderWidgetHostViewWin::HandleLockedMouseEvent(UINT message, | |
3035 WPARAM wparam, | |
3036 LPARAM lparam) { | |
3037 TRACE_EVENT0("browser", "RenderWidgetHostViewWin::HandleLockedMouseEvent"); | |
3038 DCHECK(mouse_locked_); | |
3039 | |
3040 if (message == WM_MOUSEMOVE && move_to_center_request_.pending) { | |
3041 // Ignore WM_MOUSEMOVE messages generated by | |
3042 // MoveCursorToCenterIfNecessary(). | |
3043 CPoint current_position(LOWORD(lparam), HIWORD(lparam)); | |
3044 ClientToScreen(¤t_position); | |
3045 if (move_to_center_request_.target.x() == current_position.x && | |
3046 move_to_center_request_.target.y() == current_position.y) { | |
3047 move_to_center_request_.pending = false; | |
3048 last_mouse_position_.locked_global = move_to_center_request_.target; | |
3049 return; | |
3050 } | |
3051 } | |
3052 | |
3053 ForwardMouseEventToRenderer(message, wparam, lparam); | |
3054 } | |
3055 | |
3056 LRESULT RenderWidgetHostViewWin::OnDocumentFeed(RECONVERTSTRING* reconv) { | |
3057 size_t target_offset; | |
3058 size_t target_length; | |
3059 bool has_composition; | |
3060 if (!composition_range_.is_empty()) { | |
3061 target_offset = composition_range_.GetMin(); | |
3062 target_length = composition_range_.length(); | |
3063 has_composition = true; | |
3064 } else if (selection_range_.IsValid()) { | |
3065 target_offset = selection_range_.GetMin(); | |
3066 target_length = selection_range_.length(); | |
3067 has_composition = false; | |
3068 } else { | |
3069 return 0; | |
3070 } | |
3071 | |
3072 size_t len = selection_text_.length(); | |
3073 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); | |
3074 | |
3075 if (target_offset < selection_text_offset_ || | |
3076 target_offset + target_length > selection_text_offset_ + len) { | |
3077 return 0; | |
3078 } | |
3079 | |
3080 if (!reconv) | |
3081 return need_size; | |
3082 | |
3083 if (reconv->dwSize < need_size) | |
3084 return 0; | |
3085 | |
3086 reconv->dwVersion = 0; | |
3087 reconv->dwStrLen = len; | |
3088 reconv->dwStrOffset = sizeof(RECONVERTSTRING); | |
3089 reconv->dwCompStrLen = has_composition ? target_length: 0; | |
3090 reconv->dwCompStrOffset = | |
3091 (target_offset - selection_text_offset_) * sizeof(WCHAR); | |
3092 reconv->dwTargetStrLen = target_length; | |
3093 reconv->dwTargetStrOffset = reconv->dwCompStrOffset; | |
3094 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), | |
3095 selection_text_.c_str(), len * sizeof(WCHAR)); | |
3096 | |
3097 // According to Microsft API document, IMR_RECONVERTSTRING and | |
3098 // IMR_DOCUMENTFEED should return reconv, but some applications return | |
3099 // need_size. | |
3100 return reinterpret_cast<LRESULT>(reconv); | |
3101 } | |
3102 | |
3103 LRESULT RenderWidgetHostViewWin::OnReconvertString(RECONVERTSTRING* reconv) { | |
3104 // If there is a composition string already, we don't allow reconversion. | |
3105 if (imm32_manager_->is_composing()) | |
3106 return 0; | |
3107 | |
3108 if (selection_range_.is_empty()) | |
3109 return 0; | |
3110 | |
3111 if (selection_text_.empty()) | |
3112 return 0; | |
3113 | |
3114 if (selection_range_.GetMin() < selection_text_offset_ || | |
3115 selection_range_.GetMax() > | |
3116 selection_text_offset_ + selection_text_.length()) { | |
3117 return 0; | |
3118 } | |
3119 | |
3120 size_t len = selection_range_.length(); | |
3121 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); | |
3122 | |
3123 if (!reconv) | |
3124 return need_size; | |
3125 | |
3126 if (reconv->dwSize < need_size) | |
3127 return 0; | |
3128 | |
3129 reconv->dwVersion = 0; | |
3130 reconv->dwStrLen = len; | |
3131 reconv->dwStrOffset = sizeof(RECONVERTSTRING); | |
3132 reconv->dwCompStrLen = len; | |
3133 reconv->dwCompStrOffset = 0; | |
3134 reconv->dwTargetStrLen = len; | |
3135 reconv->dwTargetStrOffset = 0; | |
3136 | |
3137 size_t offset = selection_range_.GetMin() - selection_text_offset_; | |
3138 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), | |
3139 selection_text_.c_str() + offset, len * sizeof(WCHAR)); | |
3140 | |
3141 // According to Microsft API document, IMR_RECONVERTSTRING and | |
3142 // IMR_DOCUMENTFEED should return reconv, but some applications return | |
3143 // need_size. | |
3144 return reinterpret_cast<LRESULT>(reconv); | |
3145 } | |
3146 | |
3147 LRESULT RenderWidgetHostViewWin::OnQueryCharPosition( | |
3148 IMECHARPOSITION* position) { | |
3149 DCHECK(position); | |
3150 | |
3151 if (position->dwSize < sizeof(IMECHARPOSITION)) | |
3152 return 0; | |
3153 | |
3154 RECT target_rect = {}; | |
3155 if (imm32_manager_->is_composing() && !composition_range_.is_empty() && | |
3156 position->dwCharPos < composition_character_bounds_.size()) { | |
3157 target_rect = | |
3158 composition_character_bounds_[position->dwCharPos].ToRECT(); | |
3159 } else if (position->dwCharPos == 0) { | |
3160 // When there is no on-going composition but |position->dwCharPos| is 0, | |
3161 // use the caret rect. This behavior is the same to RichEdit. In fact, | |
3162 // CUAS (Cicero Unaware Application Support) relies on this behavior to | |
3163 // implement ITfContextView::GetTextExt on top of IMM32-based applications. | |
3164 target_rect = caret_rect_.ToRECT(); | |
3165 } else { | |
3166 return 0; | |
3167 } | |
3168 ClientToScreen(&target_rect); | |
3169 | |
3170 RECT document_rect = GetPixelBounds().ToRECT(); | |
3171 ClientToScreen(&document_rect); | |
3172 | |
3173 position->pt.x = target_rect.left; | |
3174 position->pt.y = target_rect.top; | |
3175 position->cLineHeight = target_rect.bottom - target_rect.top; | |
3176 position->rcDocument = document_rect; | |
3177 return 1; | |
3178 } | |
3179 | |
3180 void RenderWidgetHostViewWin::UpdateIMEState() { | |
3181 if (base::win::IsTSFAwareRequired()) { | |
3182 ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(this); | |
3183 return; | |
3184 } | |
3185 if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE && | |
3186 text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) { | |
3187 imm32_manager_->EnableIME(m_hWnd); | |
3188 imm32_manager_->SetUseCompositionWindow(!can_compose_inline_); | |
3189 } else { | |
3190 imm32_manager_->DisableIME(m_hWnd); | |
3191 } | |
3192 | |
3193 imm32_manager_->SetTextInputMode(m_hWnd, text_input_mode_); | |
3194 } | |
3195 | |
3196 void RenderWidgetHostViewWin::UpdateInputScopeIfNecessary( | |
3197 ui::TextInputType text_input_type) { | |
3198 // The text store is responsible for handling input scope when TSF-aware is | |
3199 // required. | |
3200 if (base::win::IsTSFAwareRequired()) | |
3201 return; | |
3202 | |
3203 ui::tsf_inputscope::SetInputScopeForTsfUnawareWindow( | |
3204 m_hWnd, text_input_type, text_input_mode_); | |
3205 } | |
3206 | |
3207 //////////////////////////////////////////////////////////////////////////////// | |
3208 // RenderWidgetHostView, public: | |
3209 | |
3210 // static | |
3211 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( | |
3212 RenderWidgetHost* widget) { | |
3213 return new RenderWidgetHostViewWin(widget); | |
3214 } | |
3215 | |
3216 // static | |
3217 void RenderWidgetHostViewPort::GetDefaultScreenInfo( | |
3218 blink::WebScreenInfo* results) { | |
3219 GetScreenInfoForWindow(0, results); | |
3220 } | |
3221 | |
3222 } // namespace content | |
OLD | NEW |