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

  • 相关阅读:
    EasyUI combox实现联动
    房费制(一)——上下机、总结
    6、Cocos2dx 3.0游戏开发的基本概念找个小三场比赛
    java 集装箱 arraylist 用法
    涂料动漫学习笔记(一)
    cocos2d-x plist在拍照
    Hadoop与HBase中遇到的问题(续)java.io.IOException: Non-increasing Bloom keys异常
    Java用ZIP格式压缩和解压缩文件
    Unix/Linux环境C编程新手教程(41) C语言库函数的文件操作具体解释
    Oracle OS认证和口令文件认证方法
  • 原文地址:https://www.cnblogs.com/lenomirei/p/11945384.html
Copyright © 2020-2023  润新知