cef/tests/cefclient/browser/osr_window_win.cc

1097 lines
32 KiB
C++

// 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>
#if defined(CEF_USE_ATL)
#include <oleacc.h>
#endif
#include "include/base/cef_build.h"
#include "tests/cefclient/browser/main_context.h"
#include "tests/cefclient/browser/osr_accessibility_helper.h"
#include "tests/cefclient/browser/osr_accessibility_node.h"
#include "tests/cefclient/browser/osr_ime_handler_win.h"
#include "tests/cefclient/browser/resource.h"
#include "tests/shared/browser/geometry_util.h"
#include "tests/shared/browser/main_message_loop.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_);
// 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 = MainContext::Get()->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)
accessibility_root_ = NULL;
// 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;
#if defined(CEF_USE_ATL)
case WM_GETOBJECT: {
// Only the lower 32 bits of lParam are valid when checking the object id
// because it sometimes gets sign-extended incorrectly (but not always).
DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
// Accessibility readers will send an OBJID_CLIENT message.
if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
if (self->accessibility_root_) {
return LresultFromObject(
IID_IAccessible, wParam,
static_cast<IAccessible*>(self->accessibility_root_));
} else {
// Notify the renderer to enable accessibility.
if (self->browser_ && self->browser_->GetHost())
self->browser_->GetHost()->SetAccessibilityState(STATE_ENABLED);
}
}
} break;
#endif
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);
}
}
void OsrWindowWin::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
CEF_REQUIRE_UI_THREAD();
#if defined(CEF_USE_ATL)
if (!accessibility_handler_) {
accessibility_handler_.reset(new OsrAccessibilityHelper(value, browser_));
} else {
accessibility_handler_->UpdateAccessibilityTree(value);
}
// Update |accessibility_root_| because UpdateAccessibilityTree may have
// cleared it.
OsrAXNode* root = accessibility_handler_->GetRootNode();
accessibility_root_ = root ? root->GetNativeAccessibleObject(NULL) : NULL;
#endif // defined(CEF_USE_ATL)
}
#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