利用InjectedBundle定制自己的Webkit(二)


 

由於時間倉促,之前貼的代碼中有些許錯誤,請大家見諒,現已更正,並附上運行結果!

----------------我是開始分割線-----------------

 

在上一篇利用InjectedBundle定制自己的Webkit(一)中,我們完成了一個自己的InjectedBundle,接下來我們就要在Webkit中加載我們自己的InjectedBundle。

為了測試方便先給出一個示例的InjectedBundle代碼,項目名稱MyInjectedBundle。

#include <WebKit2/WKBundleInitialize.h>
#include <WebKit2/WKBundleFrame.h>
#include <Webkit2/WKBundlePage.h>
#include <WebKit2/WKBundle.h>
#include <Webkit2/WKUrl.h>
#include <Webkit2/WKString.h>
#include <windows.h>
#include <string.h>
#include <wchar.h>

#pragma comment(lib, "WebKit.lib")

// 框架加載完畢后調用
void didFinishLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void*)
{
  if (WKBundleFrameIsMainFrame(frame)) // 如果是主框架
  {
    WKURLRef url = WKBundleFrameCopyURL(frame); // 獲得URL
    WKStringRef str = WKURLCopyString(url); // 得到URL字符串
    size_t length = WKStringGetLength(str);
    wchar_t* urlBuffer = new wchar_t[length + 1];
    size_t size = WKStringGetCharacters(str, urlBuffer, length); // 轉成wchar_t*
    urlBuffer[length] = 0;

    WKRect rect = WKBundleFrameGetContentBounds(frame); // 得到邊界范圍
    wchar_t info[1024];
    swprintf(info, L"url: %s\nx=%f, y=%f, width=%f, height=%f",
      urlBuffer, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); // 生成結果
    ::MessageBoxW(0, info, L"FrameBound", MB_OK); // 顯示
    delete[] urlBuffer;
  }
}

// page創建完后調用
void didCreatePage(WKBundleRef bundle, WKBundlePageRef page, const void*)
{
  WKBundlePageLoaderClient loaderClient = { 0 };
  loaderClient.version = kWKBundlePageLoaderClientCurrentVersion;
  loaderClient.didFinishLoadForFrame = didFinishLoadForFrame; // 注冊回調
  WKBundlePageSetPageLoaderClient(page, &loaderClient);
}

extern "C" __declspec(dllexport)
void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)
{
  WKBundleClient client = { 0 };
  client.version = kWKBundleClientCurrentVersion;
  client.didCreatePage = didCreatePage;
  WKBundleSetClient(bundle, &client);
}

以上InjectedBundle首先注冊page創建后的回調didCreatePage,然后在page創建之后注冊框架加載完畢的回調didFinishLoadForFrame,當框架加載完畢之后再獲得框架的url和區域坐標顯示出來。

在InjectedBundle編譯生成完畢之后得到MyInjectedBundle.dll,之后我們開始寫客戶進程調用的代碼。

 

創建一個空項目WebkitDemo,准備工作同InjectedBundle,不過配置類型是應用程序(.exe)

然后開始寫代碼

首先需要建立一個讓View對象依附的Window,Window是怎樣的不重要,主要是需要一個載體和消息隊列,所以這里直接不顯示Window了。

#include <Windows.h>
#include <Webkit2/WKType.h>
#include <WebKit2/WKStringCF.h>
#include <Webkit2/WKString.h>
#include <Webkit2/WKUrl.h>
#include <Webkit2/WKView.h>
#include <Webkit2/WKContext.h>
#include <Webkit2/WKPage.h>
#include <shlwapi.h>
#include <WebKit2/WKURLCF.h>

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "CoreFoundation.lib")
#pragma comment(lib, "WebKit.lib")

LRESULT CALLBACK runLoopProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
  if (message == WM_CREATE)
  {
    createView(window);  // 創建view這里之后介紹
    return 0;
  }
  return ::DefWindowProc(window, message, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpstrCmdLine, int nCmdShow)
{
  MSG msg;
  BOOL bRet;

  WNDCLASS wc;
  wc.style = 0;
  wc.lpfnWndProc = runLoopProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wc.lpszMenuName = 0;
  wc.lpszClassName = L"MyWebkitToolRunLoop";

  if (!RegisterClass(&wc))
    return 0;

  HWND hwnd = ::CreateWindow(L"MyWebkitToolRunLoop", L"MyWebkitTool",
    WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT,
    CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);

  if (!hwnd)
    return 0;

  // 開始消息循環

  while ( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
  {
    if (bRet == -1)
    {
      break;
    }
    else
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  return 0;
}

然后在WM_CREATE事件的響應中創建view,創建view需要一個WKContextRef參數,這個參數能夠指定view在什么上下文中創建,即利用哪個WebProcess進程來創建。所以我們就可以在WKContextRef中設置使用我們的InjectedBundle。

WKStringRef getBundlePath()

{
  const wchar_t* THIS_FILE_NAME = L"WebkitDemo.exe";  // 客戶程序文件名
  const wchar_t* BUNDLE_FILE_NAME = L"MyInjectedBundle.dll";  // InjectedBundle文件名
  HMODULE thisModule = ::GetModuleHandleW(THIS_FILE_NAME);
  if (!thisModule)
    return 0;

  WCHAR pathStr[MAX_PATH];
  if (!::GetModuleFileNameW(thisModule, pathStr, (DWORD)wcslen(pathStr)))  // 得到客戶程序文件路徑
    return 0;

  ::PathRemoveFileSpecW(pathStr);  // 去掉文件名
  if (!::PathAppendW(pathStr, BUNDLE_FILE_NAME))  // 加上InjectedBundle文件名
    return 0;

  CFStringRef cf = CFStringCreateWithCharacters(0, (const UniChar*)pathStr, (CFIndex)wcslen(pathStr));

  WKStringRef res = WKStringCreateWithCFString(cf);

  CFRelease(cf);

  return res;
}

void goToURL(const wchar_t* wcurl, WKViewRef view)
{
  CFStringRef string = CFStringCreateWithCharacters(0, (const UniChar*)wcurl, (CFIndex)wcslen(wcurl));
  CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(0, string, 0, 0, kCFStringEncodingUTF8);
  CFRelease(string);
  CFURLRef cfURL = CFURLCreateWithString(0, escapedString, 0);
  CFRelease(escapedString);
  WKURLRef url = WKURLCreateWithCFURL(cfURL);  // 創建一個URL對象
  CFRelease(cfURL);
  WKPageRef page = WKViewGetPage(view);
  WKPageLoadURL(page, url);       // 開始加載
  WKRelease(url);
}

void createView(HWND window)
{
  RECT webViewRect = { 0, 0, 0, 0 };
  WKStringRef path = getBundlePath();
  WKContextRef context = WKContextCreateWithInjectedBundlePath(path);  // 利用InjectedBundle創建context對象
  WKRelease(path);

  WKViewRef view = WKViewCreate(webViewRect, context, 0, window);    // 創建view對象
  WKViewSetIsInWindow(view, true);
  // 這里可以用WKPageSetXXXClient注冊一些回調函數
  goToURL(L"http://www.baidu.com", view);
}

我們需要給將WKContextCreateWithInjectedBundlePath傳入bundle文件的路徑,為方便我們將MyInjectedBundle.dll和客戶端程序放在一起,利用getBundlePath()得到bundle文件的路徑。在創建完成之后調用goToURL讓Webkit加載頁面,這里以百度為例。

以上代碼用到了Shlwapi和CoreFoundation,所以加上頭文件和鏈接庫。

編譯運行,大功告成了!

 

之后附上源碼文件InjectedBundleDemo.zip

小小嘗試,歡迎大家多提意見和建議!下一期准備為大家介紹利用定制的Webkit來爬取動態頁面內容和動態生成的鏈接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM