OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 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 (!GetMonitorInfo(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 |