Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.
If you find these projects useful please consider becoming a sponsor with Patreon, GitHub or Liberapay.

TChromiumOnProcessMessageReceived not triggered

Post Reply
PioPio
Posts: 42
Joined: Sun Nov 05, 2017 10:25 pm

TChromiumOnProcessMessageReceived not triggered

Post by PioPio »

Hello,

I have the following source:

Code: Select all

unit MyUnit;

interface

{$I cef.inc}

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  uCEFWindowParent,
  uCEFChromiumWindow,
  uCEFChromium,
  uCEFInterfaces,
  Vcl.ExtCtrls,
  Vcl.StdCtrls,
  uCEFTypes,
  uCEFApplication,
  uCEFConstants;

const
  MINIBROWSER_VISITDOM = WM_APP + $101;

type
  TForm1 = class(TForm)
    ChromiumWindow1: TChromiumWindow;
    Timer1: TTimer;
    Chromium1: TChromium;
    procedure Timer1Timer(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
      const frame: ICefFrame; httpStatusCode: Integer);
    procedure Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
    procedure Chromium1ProcessMessageReceived(Sender: TObject; const browser: ICefBrowser;
      sourceProcess: TCefProcessId; const message: ICefProcessMessage; out Result: Boolean);
  private
    { Private declarations }
    FChromiumCreated: Boolean;
    FChromiumPageLoaded: Boolean;
    procedure WMMove(var aMessage: TWMMove); message WM_MOVE;
    procedure WMMoving(var aMessage: TMessage); message WM_MOVING;
  public
    { Public declarations }
    function IsChromiumCreated: Boolean;
    function IsChromiumPageLoaded: Boolean;
    procedure RetrieveResult;
  end;

var
  Form1: TForm1;

procedure GlobalCEFApp_OnProcessMessageReceived(const browser: ICefBrowser;
  sourceProcess: TCefProcessId; const message: ICefProcessMessage; var aHandled: boolean);

implementation

uses
  uCEFProcessMessage,
  uCEFMiscFunctions,
  uCEFSchemeRegistrar,
  uCEFRenderProcessHandler,
  uCEFv8Handler,
  uCEFDomVisitor,
  uCEFDomNode,
  uCEFTask,
  StrUtils;

{$R *.dfm}

const
  Actions: array[0..0] of string = ('RETRIEVE_RESULT');

var
  BrowserAction: Integer;

procedure TForm1.Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
begin
  FChromiumCreated := True;
end;

procedure TForm1.Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
  const frame: ICefFrame; httpStatusCode: Integer);
begin
  FChromiumPageLoaded := False;
  if frame = nil then
    exit;

  if (frame <> nil) and frame.IsMain then
  begin
    FChromiumPageLoaded := True;
  end
  else
  begin
    if httpStatusCode = 200 then
    begin
      if (browser <> nil) and (frame.IsMain = True) then
      begin
        FChromiumPageLoaded := True;
      end;
    end;
  end;
end;

procedure TForm1.Chromium1ProcessMessageReceived(Sender: TObject; const browser: ICefBrowser;
  sourceProcess: TCefProcessId; const message: ICefProcessMessage; out Result: Boolean);
begin
  Result := False;

  if (message = nil) or (message.ArgumentList = nil) then
    exit;

  if (message.Name = Actions[0]) then
  begin
//    ShowStatusText('DOM Visitor result text : ' + message.ArgumentList.GetString(0));
    Result := True;
  end;


end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FChromiumCreated := False;
  FChromiumPageLoaded := False;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  if not (Chromium1.CreateBrowser(ChromiumWindow1, '')) then
    Timer1.Enabled := True;
end;

function TForm1.IsChromiumCreated: Boolean;
begin
  Result := FChromiumCreated;
end;

function TForm1.IsChromiumPageLoaded: Boolean;
begin
  Result := FChromiumPageLoaded;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  if not (Chromium1.CreateBrowser(ChromiumWindow1, '')) and not (Chromium1.Initialized) then
    Timer1.Enabled := True
end;

procedure TForm1.RetrieveResult;
var
  TempMsg: ICefProcessMessage;
begin
  BrowserAction := 0;
  TempMsg := TCefProcessMessageRef.New(actions[0]);
  Chromium1.SendProcessMessage(PID_RENDERER, TempMsg);
end;

procedure TForm1.WMMove(var aMessage: TWMMove);
begin
  inherited;
  if (Chromium1 <> nil) then
    Chromium1.NotifyMoveOrResizeStarted;
end;

procedure TForm1.WMMoving(var aMessage: TMessage);
begin
  inherited;
  if (Chromium1 <> nil) then
    Chromium1.NotifyMoveOrResizeStarted;
end;

procedure DOMVisitor_OnDocAvailable(const browser: ICefBrowser; const document: ICefDomDocument);
var
  msg: ICefProcessMessage;
begin

  case BrowserAction of
    0:
      begin
        msg := TCefProcessMessageRef.New(actions[BrowserAction]);
        msg.ArgumentList.SetString(0, 'document.Title : ' + document.Title);
        //  SimpleNodeSearch(document);
      end;
  end;
  browser.SendProcessMessage(PID_BROWSER, msg);
end;

procedure GlobalCEFApp_OnProcessMessageReceived(const browser: ICefBrowser;
  sourceProcess: TCefProcessId; const message: ICefProcessMessage; var aHandled: boolean);
var
  TempFrame: ICefFrame;
  TempVisitor: TCefFastDomVisitor2;
begin
  if (browser <> nil) and (message.name = Actions[0]) then
  begin
    TempFrame := browser.MainFrame;

    if (TempFrame <> nil) then
    begin
      TempVisitor := TCefFastDomVisitor2.Create(browser, DOMVisitor_OnDocAvailable);
      TempFrame.VisitDom(TempVisitor);
    end;
    aHandled := True
  end
  else
    aHandled := False;
end;

end.
If I call Form1.RetrieveResult the event TForm1.Chromium1ProcessMessageReceived is not triggered. Do you know why it is ?
I looked at the DOMVisitor example but I cannot understand why.

Many thanks

Alberto
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: TChromiumOnProcessMessageReceived not triggered

Post by salvadordf »

Hi,

At first sight it looks good but I haven't debugged it.

Try to use the debugging techniques described here :
https://www.briskbard.com/index.php?lang=en&pageid=cef

Delphi can only debug one process but if you select the render process you can set breakpoints.

You can also put some CefLog calls with information about the local variables at several points in your code and then read the debug.log file.

if you use the CefLog method, you must add this before the GlobalCEFApp.StartMainProcess call in your DPR :

Code: Select all

  GlobalCEFApp.LogFile              := 'debug.log';
  GlobalCEFApp.LogSeverity          := LOGSEVERITY_ERROR;  // add uCEFTypes to the uses clause to recognize LOGSEVERITY_ERROR
PioPio
Posts: 42
Joined: Sun Nov 05, 2017 10:25 pm

Re: TChromiumOnProcessMessageReceived not triggered

Post by PioPio »

I set GlobalCEFApp.SingleProcess := True and also implemented the log file but I get an obscure:
[0121/105317.128:ERROR:url_request_context_getter_impl.cc(128)] Cannot use V8 Proxy resolver in single process mode.

Chromium1.SendProcessMessage(PID_RENDERER, TempMsg); sends the message correctly but onChromiumProcessMessageReceived does not receive anything back. Do I have to check something in the broswer or in the render first to make sure they are ready to process the message ?

Besides, I put an IDE breakpoint on GlobalCEFApp_OnProcessMessageReceived but this is highlighted in green instead of the usual red. It seems this procedure cannot accept the breakpoint.

Many thanks
Alberto
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: TChromiumOnProcessMessageReceived not triggered

Post by salvadordf »

PioPio wrote: Sun Jan 21, 2018 12:48 pm I set GlobalCEFApp.SingleProcess := True and also implemented the log file but I get an obscure:
[0121/105317.128:ERROR:url_request_context_getter_impl.cc(128)] Cannot use V8 Proxy resolver in single process mode.
Some features stop working in single process mode. Many demos in the JavaScript folder can't be debugged in that mode.
PioPio wrote: Sun Jan 21, 2018 12:48 pm Chromium1.SendProcessMessage(PID_RENDERER, TempMsg); sends the message correctly but onChromiumProcessMessageReceived does not receive anything back. Do I have to check something in the broswer or in the render first to make sure they are ready to process the message ?
Besides, I put an IDE breakpoint on GlobalCEFApp_OnProcessMessageReceived but this is highlighted in green instead of the usual red. It seems this procedure cannot accept the breakpoint.
CEF uses several processes for different tasks but Delphi can only debug one process each time.

Most of the procedures and functions in your unit are only executed in the browser process and the rest in the render process.
If you are debugging one of the processes and set a breakpoint in a code line that is executed in a different process, it won't stop the code execution.

In your unit, GlobalCEFApp_OnProcessMessageReceived and DOMVisitor_OnDocAvailable are executed in the render process. The rest belongs to the browser process.

If you debug your application normally, you will be able to set breakpoints in the procedures and functions from the browser process.

To debug GlobalCEFApp_OnProcessMessageReceived and DOMVisitor_OnDocAvailable you'll have to select the menu option Run->Run Without Debugging... in Delphi. Then click on Run->Attach To Process... and select the render subprocess.

In any case, it's much easier to add a bunch of CefLog calls throughout the functions that seem to be problematic and then check the results in the debug.log file. If you use unique text strings and variable values in the CefLog calls you will know what part of your code is never executed and why.

Remember that you can't access variables or GUI elements defined in a different process. Not even if they are global variables.
PioPio
Posts: 42
Joined: Sun Nov 05, 2017 10:25 pm

Re: TChromiumOnProcessMessageReceived not triggered

Post by PioPio »

Hi Salvador,

School boy error ! the following line was missing from the project file

Code: Select all

GlobalCEFApp.OnProcessMessageReceived := GlobalCEFApp_OnProcessMessageReceived;

I have added it and TChromiumOnProcessMessageReceived is succesfully triggered now.

I have a new issue tho. Because I have added GlobalCEFApp.OnProcessMessageReceived := GlobalCEFApp_OnProcessMessageReceived I have started having a memory leak.
I have installed the latest CEF and CEF4Delphi versions and tried again but no joy. I also ran the DOMVisitor example and the memory leak is there too (I tried a couple of other examples and they are fine).
The following is the report from MadExcept (similar to FastMM)
allocation number: 1568
program up time: 556 ms
type: GetMem
address: $695afec
size: 20
access rights: read/write

main thread ($960):
671cd312 madExcept32.dll madExceptDbg 1603 GetMemCallback
00404550 DOMVisitor.exe System 3454 @GetMem
0040598a DOMVisitor.exe System 13000 TObject.NewInstance
0040b198 DOMVisitor.exe System 30129 TInterfacedObject.NewInstance
0040602f DOMVisitor.exe System 14164 @ClassCreate
004059bf DOMVisitor.exe System 13015 TObject.Create
005e150d DOMVisitor.exe DOMVisitor 63 initialization
76a3efaa kernel32.dll BaseThreadInitThunk

memory dump:
0695afec 9c f3 5d 00 01 00 00 00 - fc 1d 40 00 10 f3 5d 00 ..].......@...].
0695affc 00 00 00 00 ....
If I remove GlobalCEFApp.OnProcessMessageReceived := GlobalCEFApp_OnProcessMessageReceived from DOMVisitor no more memory leaks.

Can you please try on your side and let me know ?

Many thanks again because with your input you put me on the right direction


Alberto
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: TChromiumOnProcessMessageReceived not triggered

Post by salvadordf »

FastMM4 shows this : 13 - 20 bytes: DOMVisitor$ActRec x 1

I think this is related :
https://stackoverflow.com/questions/928 ... aks-memory

I'll add an issue about this in GitHub.

Thanks! :D
Post Reply