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