代码模块与职责

所有的代码都在src目录下,这会导致一上手的时候无法快速划分模块,不便于理解,如果分类然后放文件夹就会好一些。

最关键的部分在于uCEFApplication,是和dll链接的部分

uCEFInterfaces.pas,可以在这个文件内找到所有关于接口类型的声明,抽象了基本类型使用的接口,结构清晰。几乎是个功能都能找到对应的接口。和cef提供的接口有高度一致性。除了cef相关的接口外,还有自定义的一些工具接口。

uCEFClient.pas,继承自ICefClient,用于实现获取Handler的接口

uCEFTypes.pas,这个文件声明了大量的类型,但是不知道是不是所有的类型声明都在这里面。

uCEFChromium.pas,存放了TChromium的类型声明,是实现功能的关键类。

uCEFLoadHandler,存放了loadHandler相关类型,回调处理器的一个具体实现,还有很多其他的handler。

uCEFApplication.pas是核心,涉及到关键部分,有关键的类TCefApplication,加载了dll并获取函数,是使用CEF4Delphi的入口。

关键类型和关键接口

ICefBrowser:主要是浏览器级别的接口,获取frame,后退前进,中断加载等接口(浏览器进程的功能接口);

ICefFrame:加载网页的对象,LoadUrl功能就是它提供的。可以看成加载网页的那个frame。针对于网页级别的接口(加载网页,复制粘贴等在网页级别的操作接口);

ICefClient:接口,提供获取各种各样Handler的接口。其中Handler是回调处理器;

IChromiumEvents:关键接口,用于执行浏览器的关键操作,和handler回调处理器一起工作,实现连接libcef.dll的事件传递,该接口是自定义接口,非cef接口;

附件区域

事件传递流程

libcef.dll中注册的回调事件是如何通知到TChromium对象的呢?

在文件uCEFChromium.pas中,TChromium对象的常用工作流程CreateBrowser,会进行handler的注册

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处理机),见下述代码

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对象传递过去)

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接口对接的注册函数的类型。

最新文章

  1. 初次启动app校验的活动图和分析
  2. JS 获取地址栏三级域名
  3. Atitit usrQBM2331 参数格式化规范
  4. 20款风格独特的搜索框 PSD 设计素材免费下载
  5. iOS应用架构谈 网络层设计方案
  6. 赤红血OL
  7. REUSE_ALV_GRID_DISPLAY显示ALV,设置可编辑时,与内表数据同步问题
  8. JS中使用EL表达式
  9. 【extjs】 Extjs中的Ext.grid.Panel隐藏列会显示在表头中解决方法
  10. MFC消息响应机制 q
  11. 百度的android采访分析
  12. LVS原理详解及部署之二:LVS原理详解(3种工作方式8种调度算法)
  13. 团队作业——Alpha冲刺之事后诸葛亮
  14. Resource 的 IsSealed 问题
  15. Python排序算法——插入排序
  16. Spring 中参数名称解析 - ParameterNameDiscoverer
  17. ThinkPHP 模型方法 getField() 和 select() 使用技巧
  18. zabbix3.2的server和zabbix-agent2.2怎么监控MySQL的办法
  19. BZOJ2806_Cheat
  20. Windows Apache Django 配置

热门文章

  1. ssh-agent的作用
  2. 达能依靠Matrikon进行数据存储和分析
  3. js 实现JSONP
  4. python实现LRU热点缓存
  5. 【Spring Boot】Spring Boot之使用ImportSelector类实现动态注册Bean
  6. Ubuntu简单配置
  7. 使用ruamel.yaml库,解析yaml文件
  8. IDEA优秀插件分享之---Mybatis Log Plugin
  9. c# 第三节 vs的安装
  10. JWT 使用的另一种声音