#include "stdafx.h" #include "cef_control.h" #include "include/cef_browser.h" #include "include/cef_frame.h" #include "include/cef_runnable.h" #include "cef_control/handler/browser_handler.h" #include "cef_control/manager/cef_manager.h" #include "cef_control/app/cef_js_bridge.h" namespace nim_comp { namespace { #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) } CefControl::CefControl(void) { } CefControl::~CefControl(void) { if (browser_handler_.get() && browser_handler_->GetBrowser().get()) { // Request that the main browser close. browser_handler_->CloseAllBrowser(); browser_handler_->SetHostWindow(NULL); browser_handler_->SetHandlerDelegate(NULL); m_pWindow->RemoveMessageFilter(this); } } void CefControl::Init() { if (browser_handler_.get() == nullptr) { m_pWindow->AddMessageFilter(this); browser_handler_ = new nim_comp::BrowserHandler; browser_handler_->SetHostWindow(m_pWindow->GetHWND()); browser_handler_->SetHandlerDelegate(this); ReCreateBrowser(); } if (!js_bridge_.get()) { js_bridge_.reset(new nim_comp::CefJSBridge); } __super::Init(); } void CefControl::ReCreateBrowser() { if (browser_handler_->GetBrowser() == nullptr) { // 使用无窗模式,离屏渲染 CefWindowInfo window_info; window_info.SetAsWindowless(m_pWindow->GetHWND(), false); CefBrowserSettings browser_settings; //browser_settings.file_access_from_file_urls = STATE_ENABLED; //browser_settings.universal_access_from_file_urls = STATE_ENABLED; CefBrowserHost::CreateBrowser(window_info, browser_handler_, L"", browser_settings, NULL); } } void CefControl::SetPos(UiRect rc) { __super::SetPos(rc); if (browser_handler_.get()) { browser_handler_->SetViewRect(rc); } } void CefControl::HandleMessage(EventArgs& event) { if (browser_handler_.get() && browser_handler_->GetBrowser().get() == NULL) return __super::HandleMessage(event); else if (event.Type == kEventInternalSetFocus) { if (browser_handler_->GetBrowserHost().get()) { browser_handler_->GetBrowserHost()->SendFocusEvent(true); } } else if (event.Type == kEventInternalKillFocus) { if (browser_handler_->GetBrowserHost().get()) { browser_handler_->GetBrowserHost()->SendFocusEvent(false); } } __super::HandleMessage(event); } void CefControl::SetVisible(bool bVisible /*= true*/) { __super::SetVisible(bVisible); if (browser_handler_.get() && browser_handler_->GetBrowserHost().get()) { browser_handler_->GetBrowserHost()->WasHidden(!bVisible); } } void CefControl::SetInternVisible(bool bVisible) { __super::SetInternVisible(bVisible); if (browser_handler_.get() && browser_handler_->GetBrowserHost().get()) { browser_handler_->GetBrowserHost()->WasHidden(!bVisible); } } void CefControl::Paint(IRenderContext* pRender, const UiRect& rcPaint) { __super::Paint(pRender, rcPaint); if (dc_cef_.IsValid() && browser_handler_.get() && browser_handler_->GetBrowser().get()) { // 绘制cef PET_VIEW类型的位图 BitBlt(pRender->GetDC(), m_rcItem.left, m_rcItem.top, m_rcItem.GetWidth(), m_rcItem.GetHeight(), dc_cef_.GetDC(), 0, 0, SRCCOPY); // 绘制cef PET_POPUP类型的位图 if (!rect_popup_.IsEmpty() && dc_cef_popup_.IsValid()) { // 假如popup窗口位置在控件的范围外,则修正到控件范围内,指绘制控件范围内的popup窗口 int paint_x = rect_popup_.x; int paint_y = rect_popup_.y; int paint_buffer_x = 0; int paint_buffer_y = 0; if (rect_popup_.x < 0) { paint_x = 0; paint_buffer_x = -rect_popup_.x; } if (rect_popup_.y < 0) { paint_y = 0; paint_buffer_y = -rect_popup_.y; } BitBlt(pRender->GetDC(), m_rcItem.left + paint_x, m_rcItem.top + paint_y, rect_popup_.width, rect_popup_.height, dc_cef_popup_.GetDC(), paint_buffer_x, paint_buffer_y, SRCCOPY); } } } void CefControl::SetWindow(ui::Window* pManager, ui::Box* pParent, bool bInit) { if (!browser_handler_) { __super::SetWindow(pManager, pParent, bInit); return; } if (m_pWindow) { m_pWindow->RemoveMessageFilter(this); __super::SetWindow(pManager, pParent, bInit); pManager->AddMessageFilter(this); } browser_handler_->SetHostWindow(pManager->GetHWND()); } LRESULT CefControl::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (!IsVisible() || !IsEnabled()) { bHandled = false; return 0; } bHandled = false; if (browser_handler_.get() == nullptr || browser_handler_->GetBrowser().get() == nullptr) return 0; switch (uMsg) { case WM_MOUSEMOVE: { return SendMouseMoveEvent(uMsg, wParam, lParam, bHandled); } break; case WM_SETCURSOR: { // 这里拦截WM_SETCURSOR消息,不让duilib处理(duilib会改变光标样式),否则会影响Cef中的鼠标光标 POINT pt = { 0 }; ::GetCursorPos(&pt); ::ScreenToClient(m_pWindow->GetHWND(), &pt); if (!m_rcItem.IsPointIn(pt)) return 0; m_pWindow->CallWindowProc(uMsg, wParam, lParam); bHandled = true; return 0; } break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: { return SendButtonDownEvent(uMsg, wParam, lParam, bHandled); } break; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: { return SendButtonUpEvent(uMsg, wParam, lParam, bHandled); } break; case WM_SYSCHAR: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_KEYDOWN: case WM_KEYUP: case WM_CHAR: { if (IsFocused()) { return SendKeyEvent(uMsg, wParam, lParam, bHandled); } } break; case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: { return SendButtonDoubleDownEvent(uMsg, wParam, lParam, bHandled); } break; case WM_CAPTURECHANGED: case WM_CANCELMODE: { return SendCaptureLostEvent(uMsg, wParam, lParam, bHandled); } break; case WM_MOUSEWHEEL: { return SendMouseWheelEvent(uMsg, wParam, lParam, bHandled); } break; case WM_MOUSELEAVE: { return SendMouseLeaveEvent(uMsg, wParam, lParam, bHandled); } break; default: bHandled = false; } return 0; } bool CefControl::AttachDevTools(Control* control) { CefControl *view = dynamic_cast(control); if (devtool_attached_ || !view) return true; auto browser = browser_handler_->GetBrowser(); auto view_browser = view->browser_handler_->GetBrowser(); if (browser == nullptr || view_browser == nullptr) { auto weak = view->GetWeakFlag(); auto task = [this, weak, view](){ nbase::ThreadManager::PostTask(kThreadUI, ToWeakCallback([this, weak, view](){ if (weak.expired()) return; AttachDevTools(view); })); }; view->browser_handler_->AddAfterCreateTask(task); } else { CefWindowInfo windowInfo; windowInfo.SetAsWindowless(GetWindow()->GetHWND(), false); CefBrowserSettings settings; browser->GetHost()->ShowDevTools(windowInfo, view_browser->GetHost()->GetClient(), settings, CefPoint()); devtool_attached_ = true; if (cb_devtool_visible_change_ != nullptr) cb_devtool_visible_change_(devtool_attached_); } return true; } ////////////////////////////////////////////////////////////////////////////////// LRESULT CefControl::SendButtonDownEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (!m_rcItem.IsPointIn(pt)) return 0; this->SetFocus(); CefMouseEvent mouse_event; mouse_event.x = pt.x - m_rcItem.left; mouse_event.y = pt.y - m_rcItem.top; mouse_event.modifiers = GetCefMouseModifiers(wParam); CefBrowserHost::MouseButtonType btnType = (uMsg == WM_LBUTTONDOWN ? MBT_LEFT : ( uMsg == WM_RBUTTONDOWN ? MBT_RIGHT : MBT_MIDDLE)); host->SendMouseClickEvent(mouse_event, btnType, false, 1); bHandled = false; return 0; } LRESULT CefControl::SendButtonDoubleDownEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (!m_rcItem.IsPointIn(pt)) return 0; CefMouseEvent mouse_event; mouse_event.x = pt.x - m_rcItem.left; mouse_event.y = pt.y - m_rcItem.top; mouse_event.modifiers = GetCefMouseModifiers(wParam); CefBrowserHost::MouseButtonType btnType = MBT_LEFT; host->SendMouseClickEvent(mouse_event, btnType, false, 2); bHandled = true; return 0; } LRESULT CefControl::SendButtonUpEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (!m_rcItem.IsPointIn(pt) && !m_pWindow->IsCaptured()) return 0; CefMouseEvent mouse_event; if (uMsg == WM_RBUTTONUP) { mouse_event.x = pt.x/* - m_rcItem.left*/; // 这里不进行坐标转换,否则右键菜单位置不正确 mouse_event.y = pt.y/* - m_rcItem.top*/; } else { mouse_event.x = pt.x - m_rcItem.left; mouse_event.y = pt.y - m_rcItem.top; } mouse_event.modifiers = GetCefMouseModifiers(wParam); CefBrowserHost::MouseButtonType btnType = (uMsg == WM_LBUTTONUP ? MBT_LEFT : ( uMsg == WM_RBUTTONUP ? MBT_RIGHT : MBT_MIDDLE)); host->SendMouseClickEvent(mouse_event, btnType, true, 1); bHandled = false; return 0; } LRESULT CefControl::SendMouseMoveEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (!m_rcItem.IsPointIn(pt) && !m_pWindow->IsCaptured()) return 0; CefMouseEvent mouse_event; mouse_event.x = pt.x - m_rcItem.left; mouse_event.y = pt.y - m_rcItem.top; mouse_event.modifiers = GetCefMouseModifiers(wParam); host->SendMouseMoveEvent(mouse_event, false); bHandled = false; return 0; } LRESULT CefControl::SendMouseWheelEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; HWND scrolled_wnd = ::WindowFromPoint(pt); if (scrolled_wnd != m_pWindow->GetHWND()) return 0; ScreenToClient(m_pWindow->GetHWND(), &pt); if (!m_rcItem.IsPointIn(pt)) return 0; int delta = GET_WHEEL_DELTA_WPARAM(wParam); CefMouseEvent mouse_event; mouse_event.x = pt.x - m_rcItem.left; mouse_event.y = pt.y - m_rcItem.top; mouse_event.modifiers = GetCefMouseModifiers(wParam); host->SendMouseWheelEvent(mouse_event, IsKeyDown(VK_SHIFT) ? delta : 0, !IsKeyDown(VK_SHIFT) ? delta : 0); bHandled = true; return 0; } LRESULT CefControl::SendMouseLeaveEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (!m_rcItem.IsPointIn(pt)) return 0; CefMouseEvent mouse_event; mouse_event.x = pt.x - m_rcItem.left; mouse_event.y = pt.y - m_rcItem.top; mouse_event.modifiers = GetCefMouseModifiers(wParam); host->SendMouseMoveEvent(mouse_event, true); bHandled = true; return 0; } LRESULT CefControl::SendKeyEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); CefKeyEvent event; event.windows_key_code = wParam; event.native_key_code = lParam; event.is_system_key = uMsg == WM_SYSCHAR || uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP; if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) event.type = KEYEVENT_RAWKEYDOWN; else if (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) event.type = KEYEVENT_KEYUP; else event.type = KEYEVENT_CHAR; event.modifiers = GetCefKeyboardModifiers(wParam, lParam); host->SendKeyEvent(event); bHandled = true; return 0; } LRESULT CefControl::SendCaptureLostEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CefRefPtr host = browser_handler_->GetBrowserHost(); host->SendCaptureLostEvent(); bHandled = true; return 0; } bool CefControl::IsKeyDown(WPARAM wparam) { return (GetKeyState(wparam) & 0x8000) != 0; } int CefControl::GetCefMouseModifiers(WPARAM wparam) { int modifiers = 0; if (wparam & MK_CONTROL) modifiers |= EVENTFLAG_CONTROL_DOWN; if (wparam & MK_SHIFT) modifiers |= EVENTFLAG_SHIFT_DOWN; if (IsKeyDown(VK_MENU)) modifiers |= EVENTFLAG_ALT_DOWN; if (wparam & MK_LBUTTON) modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; if (wparam & MK_MBUTTON) modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; if (wparam & MK_RBUTTON) modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; // Low bit set from GetKeyState indicates "toggled". if (::GetKeyState(VK_NUMLOCK) & 1) modifiers |= EVENTFLAG_NUM_LOCK_ON; if (::GetKeyState(VK_CAPITAL) & 1) modifiers |= EVENTFLAG_CAPS_LOCK_ON; return modifiers; } int CefControl::GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam) { int modifiers = 0; if (IsKeyDown(VK_SHIFT)) modifiers |= EVENTFLAG_SHIFT_DOWN; if (IsKeyDown(VK_CONTROL)) modifiers |= EVENTFLAG_CONTROL_DOWN; if (IsKeyDown(VK_MENU)) modifiers |= EVENTFLAG_ALT_DOWN; // Low bit set from GetKeyState indicates "toggled". if (::GetKeyState(VK_NUMLOCK) & 1) modifiers |= EVENTFLAG_NUM_LOCK_ON; if (::GetKeyState(VK_CAPITAL) & 1) modifiers |= EVENTFLAG_CAPS_LOCK_ON; switch (wparam) { case VK_RETURN: if ((lparam >> 16) & KF_EXTENDED) modifiers |= EVENTFLAG_IS_KEY_PAD; break; case VK_INSERT: case VK_DELETE: case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT: case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: if (!((lparam >> 16) & KF_EXTENDED)) modifiers |= EVENTFLAG_IS_KEY_PAD; break; case VK_NUMLOCK: case VK_NUMPAD0: case VK_NUMPAD1: case VK_NUMPAD2: case VK_NUMPAD3: case VK_NUMPAD4: case VK_NUMPAD5: case VK_NUMPAD6: case VK_NUMPAD7: case VK_NUMPAD8: case VK_NUMPAD9: case VK_DIVIDE: case VK_MULTIPLY: case VK_SUBTRACT: case VK_ADD: case VK_DECIMAL: case VK_CLEAR: modifiers |= EVENTFLAG_IS_KEY_PAD; break; case VK_SHIFT: if (IsKeyDown(VK_LSHIFT)) modifiers |= EVENTFLAG_IS_LEFT; else if (IsKeyDown(VK_RSHIFT)) modifiers |= EVENTFLAG_IS_RIGHT; break; case VK_CONTROL: if (IsKeyDown(VK_LCONTROL)) modifiers |= EVENTFLAG_IS_LEFT; else if (IsKeyDown(VK_RCONTROL)) modifiers |= EVENTFLAG_IS_RIGHT; break; case VK_MENU: if (IsKeyDown(VK_LMENU)) modifiers |= EVENTFLAG_IS_LEFT; else if (IsKeyDown(VK_RMENU)) modifiers |= EVENTFLAG_IS_RIGHT; break; case VK_LWIN: modifiers |= EVENTFLAG_IS_LEFT; break; case VK_RWIN: modifiers |= EVENTFLAG_IS_RIGHT; break; } return modifiers; } ////////////////////////////////////////////////////////////////////////////////// }