CEF4Delphi初识
代码模块与职责
所有的代码都在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接口对接的注册函数的类型。
CEF4Delphi初识的更多相关文章
- Android动画效果之初识Property Animation(属性动画)
		
前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...
 - 初识Hadoop
		
第一部分: 初识Hadoop 一. 谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...
 - python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)
		
一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...
 - 初识IOS,Label控件的应用。
		
初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...
 - UI篇(初识君面)
		
我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...
 - Python导出Excel为Lua/Json/Xml实例教程(一):初识Python
		
Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...
 - 初识SpringMvc
		
初识SpringMvc springMvc简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 s ...
 - 初识redis数据类型
		
初识redis数据类型 1.String(字符串) string是redis最基本的类型,一个key对应一个value. string类型是二进制安全的.意思是redis的string可以包含任何数据 ...
 - Redis初识、设计思想与一些学习资源推荐
		
一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...
 
随机推荐
- windows7系统 执行应用程序报 Error accessing specified device (Error: 2)
			
--------------------------- ---------------------------Error accessing specified device (Error: 2) - ...
 - 基于JQuery可拖动列表格插件DataTables的踩坑记
			
前言 最近项目中在使用能够拖动列调整列位置顺序的表格插件---DataTables,这也是目前我找到的唯一一种存在有这种功能的插件. 在查找使用方法的过程中发现可用案例并不多,且大多言语不详.本文将全 ...
 - ca动画
			
//动画上下文-(void)animationOfUIKit{ UIView *redView=[[UIView alloc]initWithFrame:CGRectMake(10, 10, 1 ...
 - Scrum 冲刺第五篇
			
我们是这次稳了队,队员分别是温治乾.莫少政.黄思扬.余泽端.江海灵 一.会议 1.1 29号站立式会议照片: 1.2 昨天已完成的事情 团队成员 昨日计划完成的工作: 黄思扬 活动管理模块(前端) ...
 - HDFS常用API操作 和 HDFS的I/O流操作
			
前置操作 创建maven工程,修改pom.xml文件: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...
 - [linux] shell脚本编程-统计日志文件中的设备号发通知邮件
			
1.日志文件列表 比如:/data1/logs/2019/08/15/ 10.1.1.1.log.gz 10.1.1.2.log.gz 2.统计日志中的某关键字shell脚本 zcat *.gz|gr ...
 - Debian x7中如何添加永久环境变量
			
一.进入/etc/bash.bashrc(使用文本编辑器打开) 二.在最后面添加新的环境变量 export PATH=usr/...(路径):$PATH 三.保存后,打开终端,输入source ~/. ...
 - Java结构讲解
			
Java结构有顺序结构.选择结构和循环结构. 顺序结构: 是Java的基本结构,除非特别说明,否则按顺序一句一句执行:也是最简单的结构:它是任何一个算法都离不开的一种基本算法结构. 选择结构: 1.i ...
 - Jquery ajax 同步阻塞引起的UI线程阻塞的坑(loading图片显示不出来,layer.load延迟)
			
今天想做一个点击地市用ajax重新获取数据刷新页面功能,因为ajax属于耗时操作,想在获取数据且加载页面时显示加载遮罩层,结果发现了ajax的好多坑. 例如如上栗子,我想点击按钮让遮罩层显示,ajax ...
 - linux (07)  redis详解
			
一.redis持久化RDB 1.在配置文件中添加参数,开启rdb功能 redis.conf 写入 port 6379 daemonize yes logfile /data/6379/redis.lo ...