wke是基于谷歌chrome浏览器源代码的裁剪版本,大小仅仅只有10M左右,无需依赖其他的扩展库(跟CEF的一大堆大约40M的DLL来比简直爽呆了),就可以在本地使用谷歌内核快速加载网页。网上也有基于wke在Duilib 上扩展的控件代码,其实wke头文件挺清楚的了,接口一目了然,特别是JS与C++交互的函数更是容易看懂,也没什么难的,你也可以做到的。
毕竟是裁剪库,有的功能还是没有接口来处理的(比如网页加载前、页面跳转、菜单消息……),头文件代码:
#ifndef __UIWKEWEBKIT_H_ #define __UIWKEWEBKIT_H_ #pragma once #include "wke.h" #include <string> using std::wstring; namespace DuiLib { /////////////////////////////////////////// //网页加载状态改变的回调 class CWkeWebkitLoadCallback { public: virtual void OnLoadFailed()=0; virtual void OnLoadComplete()=0; virtual void OnDocumentReady()=0; }; /////////////////////////////////////////// //网页标题、地址改变的回调 class CWkeWebkitClientCallback { public: virtual void OnTitleChange(const CDuiString& strTitle)=0; virtual void OnUrlChange(const CDuiString& strUrl)=0; }; class CWkeWebkitUI : public CControlUI, public wkeBufHandler { public: CWkeWebkitUI(void); ~CWkeWebkitUI(void); virtual void onBufUpdated (const HDC hdc,int x, int y, int cx, int cy); virtual LPCTSTR GetClass()const; virtual LPVOID GetInterface(LPCTSTR pstrName); virtual void DoEvent(TEventUI& event); virtual void DoPaint(HDC hDC, const RECT& rcPaint); virtual void SetPos(RECT rc); virtual void DoInit(); virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); ////////////////////////////////////// const wstring& GetUrl()const ; bool CanGoBack() const; bool GoBack(); bool CanGoForward() const; bool GoForward(); void StopLoad(); void Refresh(); wkeWebView GetWebView(); void SetLoadCallback(CWkeWebkitLoadCallback* pCallback); CWkeWebkitLoadCallback* GetLoadCallback(); void Navigate(LPCTSTR lpUrl); void LoadFile(LPCTSTR lpFile); void LoadHtml(LPCTSTR lpHtml); protected: void StartCheckThread(); void StopCheckThread(); static void OnTitleChange(const struct _wkeClientHandler* clientHandler, const wkeString title); static void OnUrlChange(const struct _wkeClientHandler* clientHandler, const wkeString url); private: static int m_bWebkitCount; HANDLE m_hCheckThread; wstring m_strUrl; wkeWebView m_pWebView; wkeClientHandler m_ClientHandler; CWkeWebkitLoadCallback* m_pLoadCallback; CWkeWebkitClientCallback* m_pClientCallback; }; } #endif//__UIWKEWEBKIT_H_
#include "StdAfx.h" #include "UIWkeWebkit.h" #pragma comment(lib, "DuiEx/wke/wke") namespace DuiLib{ /////////////////////////////////////////////////// //网页加载状态监测线程 DWORD WINAPI CheckThreadFun(LPVOID lpParam) { CWkeWebkitUI* pWebkitUI=(CWkeWebkitUI*)lpParam; wkeWebView pWebView=pWebkitUI->GetWebView(); if ( NULL == pWebView ) return 1; CWkeWebkitLoadCallback* pCallback=pWebkitUI->GetLoadCallback(); if ( NULL == pCallback ) return 1; bool bOver=false; while( !pWebView->isLoaded() ) { if ( !bOver && pWebView->isDocumentReady() ) { pCallback->OnDocumentReady(); bOver=true; } if ( pWebView->isLoadFailed() ) { pCallback->OnLoadFailed(); return 1; } ::Sleep(30); } if ( pWebView->isLoadComplete() ) pCallback->OnLoadComplete(); return 0; } ////////////////////////////////////////////////////// int CWkeWebkitUI::m_bWebkitCount=0; CWkeWebkitUI::CWkeWebkitUI(void) :m_pWebView(NULL) ,m_hCheckThread(NULL) ,m_pLoadCallback(NULL) ,m_pClientCallback(NULL) { if ( 0 == m_bWebkitCount ) wkeInit(); m_pWebView=wkeCreateWebView(); m_pWebView->setBufHandler(this); m_ClientHandler.onTitleChanged =&CWkeWebkitUI::OnTitleChange; m_ClientHandler.onURLChanged =&CWkeWebkitUI::OnUrlChange; m_bWebkitCount++; } CWkeWebkitUI::~CWkeWebkitUI(void) { StopCheckThread(); m_pManager->KillTimer(this); wkeDestroyWebView(m_pWebView); m_bWebkitCount--; if ( 0 == m_bWebkitCount ) wkeShutdown(); } LPCTSTR CWkeWebkitUI::GetClass() const { return L"WkeWebkitUI"; } LPVOID CWkeWebkitUI::GetInterface( LPCTSTR pstrName ) { if( _tcscmp(pstrName, _T("WkeWebkit")) == 0 ) return static_cast<CWkeWebkitUI*>(this); return CControlUI::GetInterface(pstrName); } void CWkeWebkitUI::DoEvent( TEventUI& event ) { switch( event.Type ) { case UIEVENT_SETFOCUS: if ( m_pWebView ) m_pWebView->focus(); break; case UIEVENT_KILLFOCUS: if ( m_pWebView ) m_pWebView->unfocus(); break; case UIEVENT_WINDOWSIZE: if ( m_pWebView ) m_pWebView->resize(GET_X_LPARAM(event.lParam), GET_Y_LPARAM(event.lParam)); break; case UIEVENT_CHAR: { if ( NULL == m_pWebView ) break; unsigned int charCode = event.wParam; unsigned int flags = 0; if (HIWORD(event.lParam) & KF_REPEAT) flags |= WKE_REPEAT; if (HIWORD(event.lParam) & KF_EXTENDED) flags |= WKE_EXTENDED; bool bHandled=m_pWebView->keyPress(charCode, flags, false); if ( bHandled ) return ; break; } case UIEVENT_KEYDOWN: { if ( NULL == m_pWebView ) break; unsigned int flags = 0; if (HIWORD(event.lParam) & KF_REPEAT) flags |= WKE_REPEAT; if (HIWORD(event.lParam) & KF_EXTENDED) flags |= WKE_EXTENDED; bool bHandled=m_pWebView->keyDown(event.wParam, flags, false); if ( event.wParam == VK_F5 ) Refresh(); if ( bHandled ) return ; break; } case UIEVENT_KEYUP: { if ( NULL == m_pWebView ) break; unsigned int flags = 0; if (HIWORD(event.lParam) & KF_REPEAT) flags |= WKE_REPEAT; if (HIWORD(event.lParam) & KF_EXTENDED) flags |= WKE_EXTENDED; bool bHandled=m_pWebView->keyUp(event.wParam, flags, false); if ( bHandled ) return ; break; } case UIEVENT_CONTEXTMENU: { if ( NULL == m_pWebView ) break; unsigned int flags = 0; if (event.wParam & MK_CONTROL) flags |= WKE_CONTROL; if (event.wParam & MK_SHIFT) flags |= WKE_SHIFT; if (event.wParam & MK_LBUTTON) flags |= WKE_LBUTTON; if (event.wParam & MK_MBUTTON) flags |= WKE_MBUTTON; if (event.wParam & MK_RBUTTON) flags |= WKE_RBUTTON; bool handled = m_pWebView->contextMenuEvent(event.ptMouse.x, event.ptMouse.y, flags); if ( handled ) return ; break; } case UIEVENT_MOUSEMOVE: case UIEVENT_BUTTONDOWN: case UIEVENT_BUTTONUP: case UIEVENT_RBUTTONDOWN: case UIEVENT_DBLCLICK: { HWND hWnd=m_pManager->GetPaintWindow(); if ( event.Type == UIEVENT_BUTTONDOWN ) { ::SetFocus(hWnd); SetCapture(hWnd); } else if ( event.Type == UIEVENT_BUTTONUP ) ReleaseCapture(); unsigned int flags = 0; if (event.wParam & MK_CONTROL) flags |= WKE_CONTROL; if (event.wParam & MK_SHIFT) flags |= WKE_SHIFT; if (event.wParam & MK_LBUTTON) flags |= WKE_LBUTTON; if (event.wParam & MK_MBUTTON) flags |= WKE_MBUTTON; if (event.wParam & MK_RBUTTON) flags |= WKE_RBUTTON; UINT uMsg=0; switch ( event.Type ) { case UIEVENT_BUTTONDOWN: uMsg=WM_LBUTTONDOWN; break; case UIEVENT_BUTTONUP: uMsg=WM_LBUTTONUP; break; case UIEVENT_RBUTTONDOWN: uMsg=WM_RBUTTONDOWN; break; case UIEVENT_DBLCLICK: uMsg=WM_LBUTTONDBLCLK; break; case UIEVENT_MOUSEMOVE: uMsg=WM_MOUSEMOVE; break; } bool bHandled = m_pWebView->mouseEvent(uMsg, event.ptMouse.x-m_rcItem.left, \ event.ptMouse.y-m_rcItem.top, flags); if ( bHandled ) return ; break; } case UIEVENT_TIMER: if ( m_pWebView ) m_pWebView->tick(); break; case UIEVENT_SCROLLWHEEL: { POINT pt; pt.x = LOWORD(event.lParam); pt.y = HIWORD(event.lParam); int nFlag=GET_X_LPARAM(event.wParam); int delta = (nFlag==SB_LINEDOWN)?-120:120; unsigned int flags = 0; if (event.wParam & MK_CONTROL) flags |= WKE_CONTROL; if (event.wParam & MK_SHIFT) flags |= WKE_SHIFT; if (event.wParam & MK_LBUTTON) flags |= WKE_LBUTTON; if (event.wParam & MK_MBUTTON) flags |= WKE_MBUTTON; if (event.wParam & MK_RBUTTON) flags |= WKE_RBUTTON; bool handled = m_pWebView->mouseWheel(pt.x, pt.y, delta, flags); if ( handled ) return ; break; } default: CControlUI::DoEvent(event); break; } } void CWkeWebkitUI::DoPaint( HDC hDC, const RECT& rcPaint ) { if ( m_pWebView ) { RECT rcInsert; IntersectRect(&rcInsert, &m_rcItem, &rcPaint); m_pWebView->paint(hDC, rcInsert.left, rcInsert.top, \ rcInsert.right-rcInsert.left, rcInsert.bottom-rcInsert.top, \ rcInsert.left-m_rcItem.left, rcInsert.top-m_rcItem.top, true); } } void CWkeWebkitUI::onBufUpdated( const HDC hdc,int x, int y, int cx, int cy ) { RECT rcValide={ x, y, x+cx, y+cy }; ::OffsetRect(&rcValide, m_rcItem.left, m_rcItem.top); HWND hWnd=m_pManager->GetPaintWindow(); ::InvalidateRect(hWnd, &rcValide, TRUE); } void CWkeWebkitUI::Navigate( LPCTSTR lpUrl ) { if ( m_pWebView ) { m_pWebView->loadURL(lpUrl); StartCheckThread(); } } void CWkeWebkitUI::SetPos( RECT rc ) { CControlUI::SetPos(rc); if ( m_pWebView ) m_pWebView->resize(rc.right-rc.left, rc.bottom-rc.top); } void CWkeWebkitUI::DoInit() { if ( !m_strUrl.empty() ) Navigate(m_strUrl.c_str()); m_pManager->SetTimer(this, 100, 100); } void CWkeWebkitUI::StartCheckThread() { StopCheckThread(); m_hCheckThread=::CreateThread(NULL, 0, CheckThreadFun, this, 0, NULL); } void CWkeWebkitUI::StopCheckThread() { if ( m_hCheckThread ) { if ( ::WaitForSingleObject(m_hCheckThread, 10) != WAIT_OBJECT_0 ) ::TerminateThread(m_hCheckThread, 0); ::CloseHandle(m_hCheckThread); m_hCheckThread = NULL; } } bool CWkeWebkitUI::CanGoBack() const { return m_pWebView?m_pWebView->canGoBack():false; } bool CWkeWebkitUI::GoBack() { return m_pWebView?m_pWebView->goBack():false; } bool CWkeWebkitUI::CanGoForward() const { return m_pWebView?m_pWebView->canGoForward():false; } bool CWkeWebkitUI::GoForward() { return m_pWebView?m_pWebView->goForward():false; } void CWkeWebkitUI::StopLoad() { if ( m_pWebView ) m_pWebView->stopLoading(); } void CWkeWebkitUI::Refresh() { if ( m_pWebView ) { StopCheckThread(); m_pWebView->reload(); StartCheckThread(); } } wkeWebView CWkeWebkitUI::GetWebView() { return m_pWebView; } void CWkeWebkitUI::SetLoadCallback( CWkeWebkitLoadCallback* pCallback ) { m_pLoadCallback=pCallback; } CWkeWebkitLoadCallback* CWkeWebkitUI::GetLoadCallback() { return m_pLoadCallback; } void CWkeWebkitUI::OnTitleChange( const struct _wkeClientHandler* clientHandler, const wkeString title ) { } void CWkeWebkitUI::OnUrlChange( const struct _wkeClientHandler* clientHandler, const wkeString url ) { } void CWkeWebkitUI::LoadFile( LPCTSTR lpFile ) { if ( m_pWebView ) m_pWebView->loadFile(lpFile); } void CWkeWebkitUI::LoadHtml( LPCTSTR lpHtml ) { if ( m_pWebView ) m_pWebView->loadHTML(lpHtml); } const wstring& CWkeWebkitUI::GetUrl() const { return m_strUrl; } void CWkeWebkitUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue ) { if ( _tcscmp(pstrName, _T("url")) == 0 ) m_strUrl = pstrValue; else CControlUI::SetAttribute(pstrName, pstrValue); } }
控件定义完成后,我们需要来配置XML了:
<WkeWebkit name="webkit1" url="http://192.168.1.20:92/?mainctl=chrome&version=1.2.3"/>
放在一个布局里面,指定URL即可,你也可以加上其他属性然后在SetAttribute中对这些属性进行初始化。
运行程序,一个无窗口的chrome内核网页控件就展示出来了:
wke加载网页只能用于展示以及JS交互处理,毕竟裁剪后还是要失去很多其他功能的,对于chrome内核10M的大小已经是相当的不错了,另外还有一个EaWebkit也是基于chrome的扩展,编译成DLL后只有6M左右,也是开源的,大家可以网上看看。