]> git.sesse.net Git - casparcg/blobdiff - dependencies64/cef/windows/tests/cefclient/browser/osr_window_win.cc
Upgrade CEF to 3.3029.1611.g44e39a8 / Chromium 58.0.3029.81.
[casparcg] / dependencies64 / cef / windows / tests / cefclient / browser / osr_window_win.cc
diff --git a/dependencies64/cef/windows/tests/cefclient/browser/osr_window_win.cc b/dependencies64/cef/windows/tests/cefclient/browser/osr_window_win.cc
new file mode 100644 (file)
index 0000000..e066bbc
--- /dev/null
@@ -0,0 +1,1058 @@
+// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "tests/cefclient/browser/osr_window_win.h"
+
+#include <windowsx.h>
+
+#include "include/base/cef_build.h"
+#include "tests/shared/browser/geometry_util.h"
+#include "tests/shared/browser/main_message_loop.h"
+#include "tests/cefclient/browser/resource.h"
+#include "tests/cefclient/browser/osr_ime_handler_win.h"
+#include "tests/shared/browser/util_win.h"
+
+namespace client {
+
+namespace {
+
+const wchar_t kWndClass[] = L"Client_OsrWindow";
+
+// Render at 30fps during rotation.
+const int kRenderDelay = 1000 / 30;
+
+// Helper that calls wglMakeCurrent.
+class ScopedGLContext {
+ public:
+  ScopedGLContext(HDC hdc, HGLRC hglrc, bool swap_buffers)
+    : hdc_(hdc),
+      swap_buffers_(swap_buffers) {
+    BOOL result = wglMakeCurrent(hdc, hglrc);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK(result);
+  }
+  ~ScopedGLContext() {
+    BOOL result = wglMakeCurrent(NULL, NULL);
+    DCHECK(result);
+    if (swap_buffers_) {
+      result = SwapBuffers(hdc_);
+      DCHECK(result);
+    }
+  }
+
+ private:
+  const HDC hdc_;
+  const bool swap_buffers_;
+};
+
+}  // namespace
+
+
+OsrWindowWin::OsrWindowWin(Delegate* delegate,
+                           const OsrRenderer::Settings& settings)
+    : delegate_(delegate),
+      renderer_(settings),
+      hwnd_(NULL),
+      hdc_(NULL),
+      hrc_(NULL),
+      device_scale_factor_(client::GetDeviceScaleFactor()),
+      painting_popup_(false),
+      render_task_pending_(false),
+      hidden_(false),
+      last_mouse_pos_(),
+      current_mouse_pos_(),
+      mouse_rotation_(false),
+      mouse_tracking_(false),
+      last_click_x_(0),
+      last_click_y_(0),
+      last_click_button_(MBT_LEFT),
+      last_click_count_(0),
+      last_click_time_(0),
+      last_mouse_down_on_view_(false) {
+  DCHECK(delegate_);
+}
+
+OsrWindowWin::~OsrWindowWin() {
+  CEF_REQUIRE_UI_THREAD();
+  // The native window should have already been destroyed.
+  DCHECK(!hwnd_);
+}
+
+void OsrWindowWin::CreateBrowser(HWND parent_hwnd,
+                                 const RECT& rect,
+                                 CefRefPtr<CefClient> handler,
+                                 const CefBrowserSettings& settings,
+                                 CefRefPtr<CefRequestContext> request_context,
+                                 const std::string& startup_url) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::CreateBrowser, this,
+                                   parent_hwnd, rect, handler, settings,
+                                   request_context, startup_url));
+    return;
+  }
+
+  // Create the native window.
+  Create(parent_hwnd, rect);
+
+  CefWindowInfo window_info;
+  window_info.SetAsWindowless(hwnd_, renderer_.IsTransparent());
+
+  // Create the browser asynchronously.
+  CefBrowserHost::CreateBrowser(window_info, handler, startup_url, settings,
+                                request_context);
+}
+
+void OsrWindowWin::ShowPopup(HWND parent_hwnd,
+                             int x, int y, size_t width, size_t height) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::ShowPopup, this,
+                                   parent_hwnd, x, y, width, height));
+    return;
+  }
+
+  DCHECK(browser_.get());
+
+  // Create the native window.
+  const RECT rect = {x, y,
+                     x + static_cast<int>(width),
+                     y + static_cast<int>(height)};
+  Create(parent_hwnd, rect);
+
+  // Send resize notification so the compositor is assigned the correct
+  // viewport size and begins rendering.
+  browser_->GetHost()->WasResized();
+
+  Show();
+}
+
+void OsrWindowWin::Show() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::Show, this));
+    return;
+  }
+  
+  if (!browser_)
+    return;
+
+  // Show the native window if not currently visible.
+  if (hwnd_ && !::IsWindowVisible(hwnd_))
+    ShowWindow(hwnd_, SW_SHOW);
+
+  if (hidden_) {
+    // Set the browser as visible.
+    browser_->GetHost()->WasHidden(false);
+    hidden_ = false;
+  }
+
+  // Give focus to the browser.
+  browser_->GetHost()->SendFocusEvent(true);
+}
+
+void OsrWindowWin::Hide() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::Hide, this));
+    return;
+  }
+
+  if (!browser_)
+    return;
+
+  // Remove focus from the browser.
+  browser_->GetHost()->SendFocusEvent(false);
+
+  if (!hidden_) {
+    // Set the browser as hidden.
+    browser_->GetHost()->WasHidden(true);
+    hidden_ = true;
+  }
+}
+
+void OsrWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::SetBounds, this, x, y, width,
+                                   height));
+    return;
+  }
+
+  if (hwnd_) {
+    // Set the browser window bounds.
+    ::SetWindowPos(hwnd_, NULL, x, y,
+                   static_cast<int>(width), static_cast<int>(height),
+                   SWP_NOZORDER);
+  }
+}
+
+void OsrWindowWin::SetFocus() {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::SetFocus, this));
+    return;
+  }
+
+   if (hwnd_) {
+    // Give focus to the native window.
+    ::SetFocus(hwnd_);
+  }
+}
+
+void OsrWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
+  if (!CefCurrentlyOn(TID_UI)) {
+    // Execute this method on the UI thread.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::SetDeviceScaleFactor, this,
+                                   device_scale_factor));
+    return;
+  }
+
+  if (device_scale_factor == device_scale_factor_)
+    return;
+
+  device_scale_factor_ = device_scale_factor;
+  if (browser_) {
+    browser_->GetHost()->NotifyScreenInfoChanged();
+    browser_->GetHost()->WasResized();
+  }
+}
+
+void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!hwnd_ && !hdc_ && !hrc_);
+  DCHECK(parent_hwnd);
+  DCHECK(!::IsRectEmpty(&rect));
+
+  HINSTANCE hInst = ::GetModuleHandle(NULL);
+
+  const cef_color_t background_color = renderer_.GetBackgroundColor();
+  const HBRUSH background_brush = CreateSolidBrush(
+      RGB(CefColorGetR(background_color),
+          CefColorGetG(background_color),
+          CefColorGetB(background_color)));
+
+  RegisterOsrClass(hInst, background_brush);
+
+  // Create the native window with a border so it's easier to visually identify
+  // OSR windows.
+  hwnd_ = ::CreateWindow(kWndClass, 0,
+      WS_BORDER | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
+      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
+      parent_hwnd, 0, hInst, 0);
+  CHECK(hwnd_);
+
+  client_rect_ = rect;
+
+  // Associate |this| with the window.
+  SetUserDataPtr(hwnd_, this);
+
+#if defined(CEF_USE_ATL)
+  // Create/register the drag&drop handler.
+  drop_target_ = DropTargetWin::Create(this, hwnd_);
+  HRESULT register_res = RegisterDragDrop(hwnd_, drop_target_);
+  DCHECK_EQ(register_res, S_OK);
+#endif
+
+  ime_handler_.reset(new OsrImeHandlerWin(hwnd_));
+
+  // Notify the window owner.
+  NotifyNativeWindowCreated(hwnd_);
+}
+
+void OsrWindowWin::Destroy() {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(hwnd_ != NULL);
+
+#if defined(CEF_USE_ATL)
+  // Revoke/delete the drag&drop handler.
+  RevokeDragDrop(hwnd_);
+  drop_target_ = NULL;
+#endif
+
+  DisableGL();
+
+  // Destroy the native window.
+  ::DestroyWindow(hwnd_);
+  ime_handler_.reset();
+  hwnd_ = NULL;
+}
+
+void OsrWindowWin::EnableGL() {
+  CEF_REQUIRE_UI_THREAD();
+
+  PIXELFORMATDESCRIPTOR pfd;
+  int format;
+
+  // Get the device context.
+  hdc_ = GetDC(hwnd_);
+
+  // Set the pixel format for the DC.
+  ZeroMemory(&pfd, sizeof(pfd));
+  pfd.nSize = sizeof(pfd);
+  pfd.nVersion = 1;
+  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+  pfd.iPixelType = PFD_TYPE_RGBA;
+  pfd.cColorBits = 24;
+  pfd.cDepthBits = 16;
+  pfd.iLayerType = PFD_MAIN_PLANE;
+  format = ChoosePixelFormat(hdc_, &pfd);
+  SetPixelFormat(hdc_, format, &pfd);
+
+  // Create and enable the render context.
+  hrc_ = wglCreateContext(hdc_);
+
+  ScopedGLContext scoped_gl_context(hdc_, hrc_, false);
+  renderer_.Initialize();
+}
+
+void OsrWindowWin::DisableGL() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!hdc_)
+    return;
+
+  {
+    ScopedGLContext scoped_gl_context(hdc_, hrc_, false);
+    renderer_.Cleanup();
+  }
+
+  if (IsWindow(hwnd_)) {
+    // wglDeleteContext will make the context not current before deleting it.
+    BOOL result = wglDeleteContext(hrc_);
+    ALLOW_UNUSED_LOCAL(result);
+    DCHECK(result);
+    ReleaseDC(hwnd_, hdc_);
+  }
+
+  hdc_ = NULL;
+  hrc_ = NULL;
+}
+
+void OsrWindowWin::Invalidate() {
+  CEF_REQUIRE_UI_THREAD();
+
+  // Don't post another task if the previous task is still pending.
+  if (render_task_pending_)
+    return;
+  render_task_pending_ = true;
+
+  CefPostDelayedTask(TID_UI, base::Bind(&OsrWindowWin::Render, this),
+                     kRenderDelay);
+}
+
+void OsrWindowWin::Render() {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (render_task_pending_)
+    render_task_pending_ = false;
+
+  if (!hdc_)
+    EnableGL();
+
+  ScopedGLContext scoped_gl_context(hdc_, hrc_, true);
+  renderer_.Render();
+}
+
+void OsrWindowWin::NotifyNativeWindowCreated(HWND hwnd) {
+  if (!CURRENTLY_ON_MAIN_THREAD()) {
+    // Execute this method on the main thread.
+    MAIN_POST_CLOSURE(
+        base::Bind(&OsrWindowWin::NotifyNativeWindowCreated, this, hwnd));
+    return;
+  }
+
+  delegate_->OnOsrNativeWindowCreated(hwnd);
+}
+
+// static
+void OsrWindowWin::RegisterOsrClass(HINSTANCE hInstance,
+                                    HBRUSH background_brush) {
+  // Only register the class one time.
+  static bool class_registered = false;
+  if (class_registered)
+    return;
+  class_registered = true;
+
+  WNDCLASSEX wcex;
+
+  wcex.cbSize = sizeof(WNDCLASSEX);
+  wcex.style         = CS_OWNDC;
+  wcex.lpfnWndProc   = OsrWndProc;
+  wcex.cbClsExtra    = 0;
+  wcex.cbWndExtra    = 0;
+  wcex.hInstance     = hInstance;
+  wcex.hIcon         = NULL;
+  wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
+  wcex.hbrBackground = background_brush;
+  wcex.lpszMenuName  = NULL;
+  wcex.lpszClassName = kWndClass;
+  wcex.hIconSm       = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+  RegisterClassEx(&wcex);
+}
+
+void OsrWindowWin::OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam) {
+  // We handle the IME Composition Window ourselves (but let the IME Candidates
+  // Window be handled by IME through DefWindowProc()), so clear the
+  // ISC_SHOWUICOMPOSITIONWINDOW flag:
+  lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
+  ::DefWindowProc(hwnd_, message, wParam, lParam);
+
+  // Create Caret Window if required
+  if (ime_handler_) {
+    ime_handler_->CreateImeWindow();
+    ime_handler_->MoveImeWindow();
+  }
+}
+
+void OsrWindowWin::OnIMEStartComposition() {
+  if (ime_handler_) {
+    ime_handler_->CreateImeWindow();
+    ime_handler_->MoveImeWindow();
+    ime_handler_->ResetComposition();
+  }
+}
+
+void OsrWindowWin::OnIMEComposition(UINT message, WPARAM wParam,
+                                    LPARAM lParam) {
+  if (browser_ && ime_handler_) {
+    CefString cTextStr;
+    if (ime_handler_->GetResult(lParam, cTextStr)) {
+      // Send the text to the browser. The |replacement_range| and
+      // |relative_cursor_pos| params are not used on Windows, so provide
+      // default invalid values.
+      browser_->GetHost()->ImeCommitText(cTextStr,
+                                         CefRange(UINT32_MAX, UINT32_MAX), 0);
+      ime_handler_->ResetComposition();
+      // Continue reading the composition string - Japanese IMEs send both
+      // GCS_RESULTSTR and GCS_COMPSTR.
+    }
+
+    std::vector<CefCompositionUnderline> underlines;
+    int composition_start = 0;
+
+    if (ime_handler_->GetComposition(lParam, cTextStr, underlines,
+                                     composition_start)) {
+      // Send the composition string to the browser. The |replacement_range|
+      // param is not used on Windows, so provide a default invalid value.
+      browser_->GetHost()->ImeSetComposition(cTextStr, underlines,
+          CefRange(UINT32_MAX, UINT32_MAX),
+          CefRange(composition_start,
+          static_cast<int>(composition_start + cTextStr.length())));
+
+      // Update the Candidate Window position. The cursor is at the end so
+      // subtract 1. This is safe because IMM32 does not support non-zero-width
+      // in a composition. Also,  negative values are safely ignored in
+      // MoveImeWindow
+      ime_handler_->UpdateCaretPosition(composition_start - 1);
+    } else {
+      OnIMECancelCompositionEvent();
+    }
+  }
+}
+
+void OsrWindowWin::OnIMECancelCompositionEvent() {
+  if (browser_ && ime_handler_) {
+    browser_->GetHost()->ImeCancelComposition();
+    ime_handler_->ResetComposition();
+    ime_handler_->DestroyImeWindow();
+  }
+}
+
+// static
+LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, UINT message,
+                                          WPARAM wParam, LPARAM lParam) {
+  CEF_REQUIRE_UI_THREAD();
+
+  OsrWindowWin* self = GetUserDataPtr<OsrWindowWin*>(hWnd);
+  if (!self)
+    return DefWindowProc(hWnd, message, wParam, lParam);
+
+  // We want to handle IME events before the OS does any default handling.
+  switch (message) {
+    case WM_IME_SETCONTEXT:
+      self->OnIMESetContext(message, wParam, lParam);
+      return 0;
+    case WM_IME_STARTCOMPOSITION:
+      self->OnIMEStartComposition();
+      return 0;
+    case WM_IME_COMPOSITION:
+      self->OnIMEComposition(message, wParam, lParam);
+      return 0;
+    case WM_IME_ENDCOMPOSITION:
+      self->OnIMECancelCompositionEvent();
+      // Let WTL call::DefWindowProc() and release its resources.
+      break;
+
+    case WM_LBUTTONDOWN:
+    case WM_RBUTTONDOWN:
+    case WM_MBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_RBUTTONUP:
+    case WM_MBUTTONUP:
+    case WM_MOUSEMOVE:
+    case WM_MOUSELEAVE:
+    case WM_MOUSEWHEEL:
+      self->OnMouseEvent(message, wParam, lParam);
+      break;
+
+    case WM_SIZE:
+      self->OnSize();
+      break;
+
+    case WM_SETFOCUS:
+    case WM_KILLFOCUS:
+      self->OnFocus(message == WM_SETFOCUS);
+      break;
+
+    case WM_CAPTURECHANGED:
+    case WM_CANCELMODE:
+      self->OnCaptureLost();
+      break;
+
+    case WM_SYSCHAR:
+    case WM_SYSKEYDOWN:
+    case WM_SYSKEYUP:
+    case WM_KEYDOWN:
+    case WM_KEYUP:
+    case WM_CHAR:
+      self->OnKeyEvent(message, wParam, lParam);
+      break;
+
+    case WM_PAINT:
+      self->OnPaint();
+      return 0;
+
+    case WM_ERASEBKGND:
+      if (self->OnEraseBkgnd())
+        break;
+      // Don't erase the background.
+      return 0;
+
+    case WM_NCDESTROY:
+      // Clear the reference to |self|.
+      SetUserDataPtr(hWnd, NULL);
+      self->hwnd_ = NULL;
+      break;
+  }
+
+  return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+void OsrWindowWin::OnMouseEvent(UINT message, WPARAM wParam, LPARAM lParam) {
+  CefRefPtr<CefBrowserHost> browser_host;
+  if (browser_)
+    browser_host = browser_->GetHost();
+
+  LONG currentTime = 0;
+  bool cancelPreviousClick = false;
+
+  if (message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN ||
+      message == WM_MBUTTONDOWN || message == WM_MOUSEMOVE ||
+      message == WM_MOUSELEAVE) {
+    currentTime = GetMessageTime();
+    int x = GET_X_LPARAM(lParam);
+    int y = GET_Y_LPARAM(lParam);
+    cancelPreviousClick =
+        (abs(last_click_x_ - x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2))
+        || (abs(last_click_y_ - y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2))
+        || ((currentTime - last_click_time_) > GetDoubleClickTime());
+    if (cancelPreviousClick &&
+        (message == WM_MOUSEMOVE || message == WM_MOUSELEAVE)) {
+      last_click_count_ = 0;
+      last_click_x_ = 0;
+      last_click_y_ = 0;
+      last_click_time_ = 0;
+    }
+  }
+
+  switch(message) {
+    case WM_LBUTTONDOWN:
+    case WM_RBUTTONDOWN:
+    case WM_MBUTTONDOWN: {
+      ::SetCapture(hwnd_);
+      ::SetFocus(hwnd_);
+      int x = GET_X_LPARAM(lParam);
+      int y = GET_Y_LPARAM(lParam);
+      if (wParam & MK_SHIFT) {
+        // Start rotation effect.
+        last_mouse_pos_.x = current_mouse_pos_.x = x;
+        last_mouse_pos_.y = current_mouse_pos_.y = y;
+        mouse_rotation_ = true;
+      } else {
+        CefBrowserHost::MouseButtonType btnType =
+            (message == WM_LBUTTONDOWN ? MBT_LEFT : (
+             message == WM_RBUTTONDOWN ? MBT_RIGHT : MBT_MIDDLE));
+        if (!cancelPreviousClick && (btnType == last_click_button_)) {
+          ++last_click_count_;
+        } else {
+          last_click_count_ = 1;
+          last_click_x_ = x;
+          last_click_y_ = y;
+        }
+        last_click_time_ = currentTime;
+        last_click_button_ = btnType;
+
+        if (browser_host) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = x;
+          mouse_event.y = y;
+          last_mouse_down_on_view_ = !IsOverPopupWidget(x, y);
+          ApplyPopupOffset(mouse_event.x, mouse_event.y);
+          DeviceToLogical(mouse_event, device_scale_factor_);
+          mouse_event.modifiers = GetCefMouseModifiers(wParam);
+          browser_host->SendMouseClickEvent(mouse_event, btnType, false,
+                                            last_click_count_);
+        }
+      }
+    } break;
+
+    case WM_LBUTTONUP:
+    case WM_RBUTTONUP:
+    case WM_MBUTTONUP:
+      if (GetCapture() == hwnd_)
+        ReleaseCapture();
+      if (mouse_rotation_) {
+        // End rotation effect.
+        mouse_rotation_ = false;
+        renderer_.SetSpin(0, 0);
+        Invalidate();
+      } else {
+        int x = GET_X_LPARAM(lParam);
+        int y = GET_Y_LPARAM(lParam);
+        CefBrowserHost::MouseButtonType btnType =
+            (message == WM_LBUTTONUP ? MBT_LEFT : (
+             message == WM_RBUTTONUP ? MBT_RIGHT : MBT_MIDDLE));
+        if (browser_host) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = x;
+          mouse_event.y = y;
+          if (last_mouse_down_on_view_ &&
+              IsOverPopupWidget(x, y) &&
+              (GetPopupXOffset() || GetPopupYOffset())) {
+            break;
+          }
+          ApplyPopupOffset(mouse_event.x, mouse_event.y);
+          DeviceToLogical(mouse_event, device_scale_factor_);
+          mouse_event.modifiers = GetCefMouseModifiers(wParam);
+          browser_host->SendMouseClickEvent(mouse_event, btnType, true,
+                                            last_click_count_);
+        }
+      }
+      break;
+
+    case WM_MOUSEMOVE: {
+      int x = GET_X_LPARAM(lParam);
+      int y = GET_Y_LPARAM(lParam);
+      if (mouse_rotation_) {
+        // Apply rotation effect.
+        current_mouse_pos_.x = x;
+        current_mouse_pos_.y = y;
+        renderer_.IncrementSpin(
+          current_mouse_pos_.x - last_mouse_pos_.x,
+          current_mouse_pos_.y - last_mouse_pos_.y);
+        last_mouse_pos_.x = current_mouse_pos_.x;
+        last_mouse_pos_.y = current_mouse_pos_.y;
+        Invalidate();
+      } else {
+        if (!mouse_tracking_) {
+          // Start tracking mouse leave. Required for the WM_MOUSELEAVE event to
+          // be generated.
+          TRACKMOUSEEVENT tme;
+          tme.cbSize = sizeof(TRACKMOUSEEVENT);
+          tme.dwFlags = TME_LEAVE;
+          tme.hwndTrack = hwnd_;
+          TrackMouseEvent(&tme);
+          mouse_tracking_ = true;
+        }
+
+        if (browser_host) {
+          CefMouseEvent mouse_event;
+          mouse_event.x = x;
+          mouse_event.y = y;
+          ApplyPopupOffset(mouse_event.x, mouse_event.y);
+          DeviceToLogical(mouse_event, device_scale_factor_);
+          mouse_event.modifiers = GetCefMouseModifiers(wParam);
+          browser_host->SendMouseMoveEvent(mouse_event, false);
+        }
+      }
+      break;
+    }
+
+    case WM_MOUSELEAVE: {
+      if (mouse_tracking_) {
+        // Stop tracking mouse leave.
+        TRACKMOUSEEVENT tme;
+        tme.cbSize = sizeof(TRACKMOUSEEVENT);
+        tme.dwFlags = TME_LEAVE & TME_CANCEL;
+        tme.hwndTrack = hwnd_;
+        TrackMouseEvent(&tme);
+        mouse_tracking_ = false;
+      }
+
+      if (browser_host) {
+        // Determine the cursor position in screen coordinates.
+        POINT p;
+        ::GetCursorPos(&p);
+        ::ScreenToClient(hwnd_, &p);
+
+        CefMouseEvent mouse_event;
+        mouse_event.x = p.x;
+        mouse_event.y = p.y;
+        DeviceToLogical(mouse_event, device_scale_factor_);
+        mouse_event.modifiers = GetCefMouseModifiers(wParam);
+        browser_host->SendMouseMoveEvent(mouse_event, true);
+      }
+    } break;
+
+    case WM_MOUSEWHEEL:
+      if (browser_host) {
+        POINT screen_point = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
+        HWND scrolled_wnd = ::WindowFromPoint(screen_point);
+        if (scrolled_wnd != hwnd_)
+          break;
+
+        ScreenToClient(hwnd_, &screen_point);
+        int delta = GET_WHEEL_DELTA_WPARAM(wParam);
+
+        CefMouseEvent mouse_event;
+        mouse_event.x = screen_point.x;
+        mouse_event.y = screen_point.y;
+        ApplyPopupOffset(mouse_event.x, mouse_event.y);
+        DeviceToLogical(mouse_event, device_scale_factor_);
+        mouse_event.modifiers = GetCefMouseModifiers(wParam);
+        browser_host->SendMouseWheelEvent(mouse_event,
+                                          IsKeyDown(VK_SHIFT) ? delta : 0,
+                                          !IsKeyDown(VK_SHIFT) ? delta : 0);
+      }
+      break;
+  }
+}
+
+void OsrWindowWin::OnSize() {
+  // Keep |client_rect_| up to date.
+  ::GetClientRect(hwnd_, &client_rect_);
+
+  if (browser_)
+    browser_->GetHost()->WasResized();
+}
+
+void OsrWindowWin::OnFocus(bool setFocus) {
+  if (browser_)
+    browser_->GetHost()->SendFocusEvent(setFocus);
+}
+
+void OsrWindowWin::OnCaptureLost() {
+  if (mouse_rotation_)
+    return;
+
+  if (browser_)
+    browser_->GetHost()->SendCaptureLostEvent();
+}
+
+void OsrWindowWin::OnKeyEvent(UINT message, WPARAM wParam, LPARAM lParam) {
+  if (!browser_)
+    return;
+
+  CefKeyEvent event;
+  event.windows_key_code = wParam;
+  event.native_key_code = lParam;
+  event.is_system_key = message == WM_SYSCHAR ||
+                        message == WM_SYSKEYDOWN ||
+                        message == WM_SYSKEYUP;
+
+  if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
+    event.type = KEYEVENT_RAWKEYDOWN;
+  else if (message == WM_KEYUP || message == WM_SYSKEYUP)
+    event.type = KEYEVENT_KEYUP;
+  else
+    event.type = KEYEVENT_CHAR;
+  event.modifiers = GetCefKeyboardModifiers(wParam, lParam);
+
+  browser_->GetHost()->SendKeyEvent(event);
+}
+
+void OsrWindowWin::OnPaint() {
+  // Paint nothing here. Invalidate will cause OnPaint to be called for the
+  // render handler.
+  PAINTSTRUCT ps;
+  BeginPaint(hwnd_, &ps);
+  EndPaint(hwnd_, &ps);
+
+  if (browser_)
+    browser_->GetHost()->Invalidate(PET_VIEW);
+}
+
+bool OsrWindowWin::OnEraseBkgnd() {
+  // Erase the background when the browser does not exist.
+  return (browser_ == NULL);
+}
+
+bool OsrWindowWin::IsOverPopupWidget(int x, int y) const {
+  CEF_REQUIRE_UI_THREAD();
+  const CefRect& rc = renderer_.popup_rect();
+  int popup_right = rc.x + rc.width;
+  int popup_bottom = rc.y + rc.height;
+  return (x >= rc.x) && (x < popup_right) &&
+         (y >= rc.y) && (y < popup_bottom);
+}
+
+int OsrWindowWin::GetPopupXOffset() const {
+  CEF_REQUIRE_UI_THREAD();
+  return renderer_.original_popup_rect().x - renderer_.popup_rect().x;
+}
+
+int OsrWindowWin::GetPopupYOffset() const {
+  CEF_REQUIRE_UI_THREAD();
+  return renderer_.original_popup_rect().y - renderer_.popup_rect().y;
+}
+
+void OsrWindowWin::ApplyPopupOffset(int& x, int& y) const {
+  if (IsOverPopupWidget(x, y)) {
+    x += GetPopupXOffset();
+    y += GetPopupYOffset();
+  }
+}
+
+void OsrWindowWin::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  DCHECK(!browser_);
+  browser_ = browser;
+
+  if (hwnd_) {
+    // Show the browser window. Called asynchronously so that the browser has
+    // time to create associated internal objects.
+    CefPostTask(TID_UI, base::Bind(&OsrWindowWin::Show, this));
+  }
+}
+
+void OsrWindowWin::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
+  CEF_REQUIRE_UI_THREAD();
+  // Detach |this| from the ClientHandlerOsr.
+  static_cast<ClientHandlerOsr*>(browser_->GetHost()->GetClient().get())->
+      DetachOsrDelegate();
+  browser_ = NULL;
+  Destroy();
+}
+
+bool OsrWindowWin::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
+                                     CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+  return false;
+}
+
+bool OsrWindowWin::GetViewRect(CefRefPtr<CefBrowser> browser,
+                               CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+
+  rect.x = rect.y = 0;
+  rect.width = DeviceToLogical(client_rect_.right - client_rect_.left,
+                               device_scale_factor_);
+  rect.height = DeviceToLogical(client_rect_.bottom - client_rect_.top,
+                                device_scale_factor_);
+  return true;
+}
+
+bool OsrWindowWin::GetScreenPoint(CefRefPtr<CefBrowser> browser,
+                                  int viewX,
+                                  int viewY,
+                                  int& screenX,
+                                  int& screenY) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!::IsWindow(hwnd_))
+    return false;
+
+  // Convert the point from view coordinates to actual screen coordinates.
+  POINT screen_pt = {
+      LogicalToDevice(viewX, device_scale_factor_),
+      LogicalToDevice(viewY, device_scale_factor_)
+  };
+  ClientToScreen(hwnd_, &screen_pt);
+  screenX = screen_pt.x;
+  screenY = screen_pt.y;
+  return true;
+}
+
+bool OsrWindowWin::GetScreenInfo(CefRefPtr<CefBrowser> browser,
+                                 CefScreenInfo& screen_info) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!::IsWindow(hwnd_))
+    return false;
+
+  CefRect view_rect;
+  GetViewRect(browser, view_rect);
+
+  screen_info.device_scale_factor = device_scale_factor_;
+
+  // The screen info rectangles are used by the renderer to create and position
+  // popups. Keep popups inside the view rectangle.
+  screen_info.rect = view_rect;
+  screen_info.available_rect = view_rect;
+  return true;
+}
+
+void OsrWindowWin::OnPopupShow(CefRefPtr<CefBrowser> browser,
+                               bool show) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!show) {
+    renderer_.ClearPopupRects();
+    browser->GetHost()->Invalidate(PET_VIEW);
+  }
+  renderer_.OnPopupShow(browser, show);
+}
+
+void OsrWindowWin::OnPopupSize(CefRefPtr<CefBrowser> browser,
+                               const CefRect& rect) {
+  CEF_REQUIRE_UI_THREAD();
+
+  renderer_.OnPopupSize(browser, LogicalToDevice(rect, device_scale_factor_));
+}
+
+void OsrWindowWin::OnPaint(CefRefPtr<CefBrowser> browser,
+                           CefRenderHandler::PaintElementType type,
+                           const CefRenderHandler::RectList& dirtyRects,
+                           const void* buffer,
+                           int width,
+                           int height) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (painting_popup_) {
+    renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+    return;
+  }
+  if (!hdc_)
+    EnableGL();
+
+  ScopedGLContext scoped_gl_context(hdc_, hrc_, true);
+  renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
+  if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
+    painting_popup_ = true;
+    browser->GetHost()->Invalidate(PET_POPUP);
+    painting_popup_ = false;
+  }
+  renderer_.Render();
+}
+
+void OsrWindowWin::OnCursorChange(
+    CefRefPtr<CefBrowser> browser,
+    CefCursorHandle cursor,
+    CefRenderHandler::CursorType type,
+    const CefCursorInfo& custom_cursor_info) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (!::IsWindow(hwnd_))
+    return;
+
+  // Change the plugin window's cursor.
+  SetClassLongPtr(hwnd_, GCLP_HCURSOR,
+                  static_cast<LONG>(reinterpret_cast<LONG_PTR>(cursor)));
+  SetCursor(cursor);
+}
+
+bool OsrWindowWin::StartDragging(
+    CefRefPtr<CefBrowser> browser,
+    CefRefPtr<CefDragData> drag_data,
+    CefRenderHandler::DragOperationsMask allowed_ops,
+    int x, int y) {
+  CEF_REQUIRE_UI_THREAD();
+
+#if defined(CEF_USE_ATL)
+  if (!drop_target_)
+    return false;
+
+  current_drag_op_ = DRAG_OPERATION_NONE;
+  CefBrowserHost::DragOperationsMask result =
+      drop_target_->StartDragging(browser, drag_data, allowed_ops, x, y);
+  current_drag_op_ = DRAG_OPERATION_NONE;
+  POINT pt = {};
+  GetCursorPos(&pt);
+  ScreenToClient(hwnd_, &pt);
+
+  browser->GetHost()->DragSourceEndedAt(
+      DeviceToLogical(pt.x, device_scale_factor_),
+      DeviceToLogical(pt.y, device_scale_factor_),
+      result);
+  browser->GetHost()->DragSourceSystemDragEnded();
+  return true;
+#else
+  // Cancel the drag. The dragging implementation requires ATL support.
+  return false;
+#endif
+}
+
+void OsrWindowWin::UpdateDragCursor(
+    CefRefPtr<CefBrowser> browser,
+    CefRenderHandler::DragOperation operation) {
+  CEF_REQUIRE_UI_THREAD();
+
+#if defined(CEF_USE_ATL)
+  current_drag_op_ = operation;
+#endif
+}
+
+void OsrWindowWin::OnImeCompositionRangeChanged(
+    CefRefPtr<CefBrowser> browser,
+    const CefRange& selection_range,
+    const CefRenderHandler::RectList& character_bounds) {
+  CEF_REQUIRE_UI_THREAD();
+
+  if (ime_handler_) {
+    // Convert from view coordinates to device coordinates.
+    CefRenderHandler::RectList device_bounds;
+    CefRenderHandler::RectList::const_iterator it = character_bounds.begin();
+    for (; it != character_bounds.end(); ++it) {
+      device_bounds.push_back(LogicalToDevice(*it, device_scale_factor_));
+    }
+
+    ime_handler_->ChangeCompositionRange(selection_range, device_bounds);
+  }
+}
+
+#if defined(CEF_USE_ATL)
+
+CefBrowserHost::DragOperationsMask
+OsrWindowWin::OnDragEnter(CefRefPtr<CefDragData> drag_data,
+                          CefMouseEvent ev,
+                          CefBrowserHost::DragOperationsMask effect) {
+  if (browser_) {
+    DeviceToLogical(ev, device_scale_factor_);
+    browser_->GetHost()->DragTargetDragEnter(drag_data, ev, effect);
+    browser_->GetHost()->DragTargetDragOver(ev, effect);
+  }
+  return current_drag_op_;
+}
+
+CefBrowserHost::DragOperationsMask
+OsrWindowWin::OnDragOver(CefMouseEvent ev,
+                         CefBrowserHost::DragOperationsMask effect) {
+  if (browser_) {
+    DeviceToLogical(ev, device_scale_factor_);
+    browser_->GetHost()->DragTargetDragOver(ev, effect);
+  }
+  return current_drag_op_;
+}
+
+void OsrWindowWin::OnDragLeave() {
+  if (browser_)
+    browser_->GetHost()->DragTargetDragLeave();
+}
+
+CefBrowserHost::DragOperationsMask
+OsrWindowWin::OnDrop(CefMouseEvent ev,
+                     CefBrowserHost::DragOperationsMask effect) {
+  if (browser_) {
+    DeviceToLogical(ev, device_scale_factor_);
+    browser_->GetHost()->DragTargetDragOver(ev, effect);
+    browser_->GetHost()->DragTargetDrop(ev);
+  }
+  return current_drag_op_;
+}
+
+#endif  // defined(CEF_USE_ATL)
+
+}  // namespace client