3

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!

BigONotation
  • 4,406
  • 5
  • 43
  • 72
  • @MartynA So I checked both project and they both compile to 64bit Windows exes. I am also using a specific 64bit CEF3. I have not checked if this works in 32 bits but building 64bit exes should really not be a problem (since the main process manages to successfully load CEF3 and display the browser view) – BigONotation Sep 11 '16 at 17:08
  • @MartynAActually thanks for the advice! I'm gonna try to attach to the sub-process...but somehow I have the feeling that the render process handler does not get initialized at all... – BigONotation Sep 11 '16 at 17:25
  • Tried to attach to subprocess and as expected the execute method of the render handler is never called. I suspect the main process does not "see" the subprocess render handler...hence so come kind of initialization problem in the subprocess – BigONotation Sep 11 '16 at 17:29
  • Btw, I was assuming you've traced into the cef code to the point where it should load and execute your sub-process. You have done that, right? – MartynA Sep 11 '16 at 18:57
  • @MartynAN Yes I have set a breakpoint inside a CefInitialize function called during CEF3 initialization, which in turn calls a cef_execute_process function for starting the subprocess. Both functions return no error. – BigONotation Sep 11 '16 at 21:00
  • @MartynA Figured it out finally. Please see answer below. – BigONotation Sep 11 '16 at 22:57

1 Answers1

2

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.

BigONotation
  • 4,406
  • 5
  • 43
  • 72
  • Glad you figured it out, +1 – MartynA Sep 12 '16 at 06:33
  • Hi, I came up to this issue myself. Your solution seems to work fine. But could you explain how we should do it the other way around? Creating a `ICefApp`, assigning the custom handler, etc...? – Vassilis Nov 01 '16 at 03:09
  • @VassilisGr Sorry I did not understand your question? What do you mean by the other way around? – BigONotation Nov 01 '16 at 15:04
  • I mean "create an ICefApp app instance and assign the custom render process handler to this app" without the "hijacked" approach. Thanks. – Vassilis Nov 01 '16 at 16:44
  • @VassilisGr OK I understand better. Some time after my initial post the author of fpcef3 actually pointed to me by email that the approach was correct and I was not hijacking anything. In other words you can use the ICefApp in your own code as is. I can check again if you wish. – BigONotation Nov 01 '16 at 22:47
  • Fine! thank you very much. I just worried if something would malfunction. – Vassilis Nov 02 '16 at 16:37