全部的代码都在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接口对接的注册函数的类型。