Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement in freepascal/lazarus a cef3 render process handler in a subprocess

I am trying to implement a fpcef3 render process handler as a subprocess:

following the examples provided on the fpcef3 github repo, I have managed to create a render process handler subprocess:

Program subprocess;

{$mode objfpc}{$H+}

Uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  cef3lib, cef3types, cef3api, Handler;

Var
  Args : TCefMainArgs;

begin
  CefLoadLibrary;
  CefRenderProcessHandler := TCustomRenderProcessHandler.Create;

  {$IFDEF WINDOWS}
  Args.instance := HINSTANCE();

  Halt(cef_execute_process(@Args, nil, nil));
  {$ELSE}
  Args.argc := argc;
  Args.argv := argv;

  Halt(cef_execute_process(@Args, nil, nil));
  {$ENDIF}
end. 

The TCustomRenderProcessHandler is identical to the handler provided with the JavaScript example in examples subdir:

Unit Handler;

{$MODE objfpc}{$H+}

(*
 * Everything in here is called from a render process, so there is no access to GUI and all the
 * data of the main process.
 *)

Interface

Uses
  Classes, SysUtils,
  cef3types, cef3intf, cef3ref, cef3own, cef3lib;

Type
  { Custom handler for the render process }
  TCustomRenderProcessHandler = class(TCefRenderProcessHandlerOwn)
  protected
    // Test Window Bindings
    procedure OnContextCreated(const browser: ICefBrowser; const frame: ICefFrame; const context: ICefv8Context); override;
    // Test Extension
    procedure OnWebKitInitialized; override;
  end;

  TMyHandler = class(TCefv8HandlerOwn)
  protected
  function Execute(const name: ustring; const obj: ICefv8Value;
    const arguments: ICefv8ValueArray; var retval: ICefv8Value;
    var exception: ustring): Boolean; override;
  end;

Implementation

Var
  mystr : String;

{ TMyHandler }

function TMyHandler.Execute(const name : ustring; const obj : ICefv8Value;
  const arguments : ICefv8ValueArray; var retval : ICefv8Value;
  var exception : ustring) : Boolean;
begin
  // return a value
  //retval := TCefv8ValueRef.NewString('TMyHandler');
  retval := TCefv8ValueRef.NewDate(Now);

  Result := True;
end;

{ TCustomRenderProcessHandler }

procedure TCustomRenderProcessHandler.OnContextCreated(const browser : ICefBrowser;
  const frame : ICefFrame; const context : ICefv8Context);
Var
  myWin : ICefv8Value;
  args  : ICefv8ValueArray;
begin
  myWin := context.GetGlobal;
  mystr := 'a test string';
  SetLength(args, 1);
  args[0] := TCefv8ValueRef.NewString(mystr);
  myWin.SetValueByKey('myval', args[0], []);
end;

procedure TCustomRenderProcessHandler.OnWebKitInitialized;
Var
  Code: ustring;
begin
  Code :=
   'var cef;'+
   'if (!cef)'+
   '  cef = {};'+
   'if (!cef.test)'+
   '  cef.test = {};'+
   '(function() {'+
   '  cef.test.__defineGetter__(''test_param'', function() {'+
   '    native function GetTestParam();'+
   '    return GetTestParam();'+
   '  });'+
   '  cef.test.__defineSetter__(''test_param'', function(b) {'+
   '    native function SetTestParam();'+
   '    if(b) SetTestParam(b);'+
   '  });'+
   '  cef.test.test_object = function() {'+
   '    native function GetTestObject();'+
   '    return GetTestObject();'+
   '  };'+
   '})();';

  CefRegisterExtension('example/v8', Code, TMyHandler.Create as ICefv8Handler);
end;

end.

And finally in the main form of my master process, I am providing the path to the subprocess:

procedure TForm1.FormCreate(Sender: TObject);
begin
  CefSingleProcess := False;

  //CefRenderProcessHandler := TCustomRenderProcessHandler.Create;
  CefBrowserSubprocessPath := 'C:\Users\aludin\fpCEF3-master\Examples\SubProcess\subprocess64.exe'
end;     

When I launch the main app, the chromium browser is displayed correctly, however the handler does not get called (I can see however that the subprocess is launched). What am I doing wrong when initializing the handler?

Thanks for your help!

like image 940
BigONotation Avatar asked Dec 06 '25 21:12

BigONotation


1 Answers

OK finally figured it out and I feel like adding a couple of curse-words here after having spent so much time on this.

After having diggged a bit in the fpcef3 sources and single-stepping the main process, I realized that you need to create an ICefApp app instance and assign the custom render process handler to this app. So to simplify my life and avoid implementing the ICefApp interface, I "hijacked" the class used internally in the main process. The updated subprocess implementation is now given by the following code:

Program subprocess;

{$mode objfpc}{$H+}

Uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  cef3lib, cef3types, cef3api, cef3own, cef3intf, Handler;

Var
  Args : TCefMainArgs;
  app : ICefApp;

begin
  CefLoadLibrary;
  CefRenderProcessHandler := TCustomRenderProcessHandler.Create;
  app := TInternalApp.Create;
  {$IFDEF WINDOWS}
  Args.instance := HINSTANCE();

  Halt(cef_execute_process(@Args, CefGetData(app), nil));
  {$ELSE}
  Args.argc := argc;
  Args.argv := argv;

  Halt(cef_execute_process(@Args, CefGetData(app), nil));
  {$ENDIF}
end.

Now the ICefApp instance will use the render process handler assigned to CefRenderProcessHandler. Finally note how cef_execute_process() has been modified to take the app as an additional parameter.

like image 127
BigONotation Avatar answered Dec 08 '25 19:12

BigONotation



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!