全部的代碼都在src目錄下,這會致使一上手的時候沒法快速劃分模塊,不便於理解,若是分類而後放文件夾就會好一些。api
最關鍵的部分在於uCEFApplication,是和dll連接的部分瀏覽器
uCEFInterfaces.pas
,能夠在這個文件內找到全部關於接口類型的聲明,抽象了基本類型使用的接口,結構清晰。幾乎是個功能都能找到對應的接口。和cef提供的接口有高度一致性。除了cef相關的接口外,還有自定義的一些工具接口。
uCEFClient.pas
,繼承自ICefClient,用於實現獲取Handler的接口
uCEFTypes.pas
,這個文件聲明瞭大量的類型,可是不知道是否是全部的類型聲明都在這裏面。
uCEFChromium.pas
,存放了TChromium的類型聲明,是實現功能的關鍵類。
uCEFLoadHandler
,存放了loadHandler相關類型,回調處理器的一個具體實現,還有不少其餘的handler。
uCEFApplication.pas
是核心,涉及到關鍵部分,有關鍵的類TCefApplication
,加載了dll並獲取函數,是使用CEF4Delphi的入口。app
ICefBrowser
:主要是瀏覽器級別的接口,獲取frame,後退前進,中斷加載等接口(瀏覽器進程的功能接口);
ICefFrame
:加載網頁的對象,LoadUrl功能就是它提供的。能夠當作加載網頁的那個frame。針對於網頁級別的接口(加載網頁,複製粘貼等在網頁級別的操做接口);
ICefClient
:接口,提供獲取各類各樣Handler的接口。其中Handler是回調處理器;
IChromiumEvents
:關鍵接口,用於執行瀏覽器的關鍵操做,和handler回調處理器一塊兒工做,實現鏈接libcef.dll的事件傳遞,該接口是自定義接口,非cef接口;函數
libcef.dll中註冊的回調事件是如何通知到TChromium對象的呢?工具
在文件uCEFChromium.pas
中,TChromium
對象的經常使用工做流程CreateBrowser
,會進行handler的註冊oop
function TChromium.CreateBrowser( aParentHandle : HWND; aParentRect : TRect; const aWindowName : ustring; const aContext : ICefRequestContext; const aExtraInfo : ICefDictionaryValue) : boolean; begin Result := False; try // GlobalCEFApp.GlobalContextInitialized has to be TRUE before creating any browser // even if you use a custom request context. // If you create a browser in the initialization of your app, make sure you call this // function when GlobalCEFApp.GlobalContextInitialized is TRUE. // Use the GlobalCEFApp.OnContextInitialized event to know when // GlobalCEFApp.GlobalContextInitialized is set to TRUE. if not(csDesigning in ComponentState) and not(FClosing) and (FBrowser = nil) and (FBrowserId = 0) and (GlobalCEFApp <> nil) and GlobalCEFApp.GlobalContextInitialized and CreateClientHandler(aParentHandle = 0) then begin GetSettings(FBrowserSettings); InitializeWindowInfo(aParentHandle, aParentRect, aWindowName); if GlobalCEFApp.MultiThreadedMessageLoop then Result := CreateBrowserHost(@FWindowInfo, FDefaultUrl, @FBrowserSettings, aExtraInfo, aContext) else Result := CreateBrowserHostSync(@FWindowInfo, FDefaultUrl, @FBrowserSettings, aExtraInfo, aContext); end; except on e : exception do if CustomExceptionHandler('TChromium.CreateBrowser', e) then raise; end; end;
其中就有使用到CreateClientHandler
這個函數(這裏的Client說的就是繼承自ICefClient類型,是否和cef的client相似還有待商榷)
而CreateClientHandler
會調用TCustomClientHandler.Create(Self);
建立TCustomClientHandler
的對象,並且這個函數的傳參就是TChromium這個對象自身(由於TChromium繼承自IChromiumEvents表示event處理機),見下述代碼this
function TChromium.CreateClientHandler(aIsOSR : boolean) : boolean; begin Result := False; try if (FHandler = nil) then begin FIsOSR := aIsOsr; FHandler := TCustomClientHandler.Create(Self); Result := True; end; except on e : exception do if CustomExceptionHandler('TChromium.CreateClientHandler', e) then raise; end; end;
TCustomClientHandler
的對象在構造的同事會再建立一堆handler的對象(我jio得TCustomClientHandler
像是一個封裝,封裝了多個handler並對它們進行管理),以其中的OnLoadError
爲例(這個事件是當加載一個網頁失敗的時候會觸發),TCustomClientHandler
會建立一個TCustomLoadHandler
類型的對象(固然依舊是把TChromium對象傳遞過去)code
constructor TCustomClientHandler.Create(const events : IChromiumEvents; aDevToolsClient : boolean); begin inherited Create; InitializeVars; FEvents := Pointer(events); if (events <> nil) then begin if aDevToolsClient then begin if events.MustCreateKeyboardHandler then FKeyboardHandler := TCustomKeyboardHandler.Create(events); end else begin if events.MustCreateLoadHandler then FLoadHandler := TCustomLoadHandler.Create(events); if events.MustCreateFocusHandler then FFocusHandler := TCustomFocusHandler.Create(events); if events.MustCreateContextMenuHandler then FContextMenuHandler := TCustomContextMenuHandler.Create(events); if events.MustCreateDialogHandler then FDialogHandler := TCustomDialogHandler.Create(events); if events.MustCreateKeyboardHandler then FKeyboardHandler := TCustomKeyboardHandler.Create(events); if events.MustCreateDisplayHandler then FDisplayHandler := TCustomDisplayHandler.Create(events); if events.MustCreateDownloadHandler then FDownloadHandler := TCustomDownloadHandler.Create(events); if events.MustCreateJsDialogHandler then FJsDialogHandler := TCustomJsDialogHandler.Create(events); if events.MustCreateLifeSpanHandler then FLifeSpanHandler := TCustomLifeSpanHandler.Create(events); if events.MustCreateRenderHandler then FRenderHandler := TCustomRenderHandler.Create(events); if events.MustCreateRequestHandler then FRequestHandler := TCustomRequestHandler.Create(events); if events.MustCreateDragHandler then FDragHandler := TCustomDragHandler.Create(events); if events.MustCreateFindHandler then FFindHandler := TCustomFindHandler.Create(events); if events.MustCreateAudioHandler then FAudioHandler := TCustomAudioHandler.Create(events); end; end; end;
在文件uCEFLoadHandler.pas
中,則定義了相關函數對象
procedure TCustomLoadHandler.OnLoadError(const browser : ICefBrowser; const frame : ICefFrame; errorCode : TCefErrorCode; const errorText : ustring; const failedUrl : ustring); begin if (FEvents <> nil) then IChromiumEvents(FEvents).doOnLoadError(browser, frame, errorCode, errorText, failedUrl); end;
其中FEvents就是TChromium的對象(類型轉換後調用),這樣就把TChromium對象的事件處理函數鏈接到這裏來了。繼承
以後再往前追溯。是如何把delphi的函數註冊給C接口的dll的。在文件uCEFLoadHandler.pas
中,有下面這麼一個函數,命名風格和delphi大相徑庭,初步懷疑就是它被註冊的
procedure cef_load_handler_on_load_error( self : PCefLoadHandler; browser : PCefBrowser; frame : PCefFrame; errorCode : TCefErrorCode; const errorText : PCefString; const failedUrl : PCefString); stdcall; var TempObject : TObject; begin TempObject := CefGetObject(self); if (TempObject <> nil) and (TempObject is TCefLoadHandlerOwn) then TCefLoadHandlerOwn(TempObject).OnLoadError(TCefBrowserRef.UnWrap(browser), TCefFrameRef.UnWrap(frame), errorCode, CefString(errorText), CefString(failedUrl)); end;
它惟一被引用的地方以下
constructor TCefLoadHandlerOwn.Create; begin inherited CreateData(SizeOf(TCefLoadHandler)); with PCefLoadHandler(FData)^ do begin on_loading_state_change := {$IFDEF FPC}@{$ENDIF}cef_load_handler_on_loading_state_change; on_load_start := {$IFDEF FPC}@{$ENDIF}cef_load_handler_on_load_start; on_load_end := {$IFDEF FPC}@{$ENDIF}cef_load_handler_on_load_end; on_load_error := {$IFDEF FPC}@{$ENDIF}cef_load_handler_on_load_error; end; end;
inherited CreateData(SizeOf(TCefLoadHandler));
表明調用父類的CreateData函數,主要目的是開闢一塊內存空間,大小是TCefLoadHandler
類型大小那麼大的內存空間,而FData就是指向那塊內存的地址。
其中on_load_error
的定義以下on_load_error : procedure(self: PCefLoadHandler; browser: PCefBrowser; frame: PCefFrame; errorCode: TCefErrorCode; const errorText, failedUrl: PCefString); stdcall;
是一個函數對象
而TCefLoadHandlerOwn
則是繼承自下面這個類型
// /include/capi/cef_load_handler_capi.h (cef_load_handler_t) TCefLoadHandler = record base : TCefBaseRefCounted; on_loading_state_change : procedure(self: PCefLoadHandler; browser: PCefBrowser; isLoading, canGoBack, canGoForward: Integer); stdcall; on_load_start : procedure(self: PCefLoadHandler; browser: PCefBrowser; frame: PCefFrame; transition_type: TCefTransitionType); stdcall; on_load_end : procedure(self: PCefLoadHandler; browser: PCefBrowser; frame: PCefFrame; httpStatusCode: Integer); stdcall; on_load_error : procedure(self: PCefLoadHandler; browser: PCefBrowser; frame: PCefFrame; errorCode: TCefErrorCode; const errorText, failedUrl: PCefString); stdcall; end;
根據這裏的註釋,找到了cef中的c接口描述文件,發現TCefLoadHandler
類型和c接口定義的_cef_load_handler_t
結構體一致,到此處就基本能肯定了,TCefLoadHandler
就是和C接口對接的註冊函數的類型。