delphichromium-embeddeddelphi-10-seattlecef4delphi

Chromium Embedded: TCefDomVisitorOwn.visit() method never is executed


I'm using CEF4Delphi and trying get a determinated html input element of a page to after set a value to the same using the code below, but happens that the method TElementNameVisitor.visit(const document: ICefDomDocument); never is executed.

How i can solve this?

uses
uCEFChromium, uCEFWindowParent,
  uCEFChromiumWindow, uCEFInterfaces, uCEFDomVisitor;

type
  TElementNameVisitor = class(TCefDomVisitorOwn)
  private
    FName: string;
  protected
    procedure visit(const document: ICefDomDocument); override;
  public
    constructor Create(const AName: string); reintroduce;
  end;

  type
  TForm2 = class(TForm)
    Chromium1: TChromium;
    CEFWindowParent1: TCEFWindowParent;
    procedure FormShow(Sender: TObject);
    procedure Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
      const frame: ICefFrame; httpStatusCode: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

constructor TElementNameVisitor.Create(const AName: string);
begin
  inherited Create;
  FName := AName;
end;

procedure ProcessElementsByName(const AFrame: ICefFrame; const AName: string);
var
  Visitor: TElementNameVisitor;
begin
  if Assigned(AFrame) then
  begin
    Visitor := TElementNameVisitor.Create(AName);
    AFrame.VisitDom(Visitor);
  end;
end;

procedure TElementNameVisitor.visit(const document: ICefDomDocument);

  procedure ProcessNode(ANode: ICefDomNode);
  var
    Node: ICefDomNode;
  begin
    if Assigned(ANode) then
    begin
      Node := ANode.FirstChild;
      while Assigned(Node) do
      begin
        if Node.GetElementAttribute('name') = FName then
        begin
          Node.SetElementAttribute('value', '-15.792253570362445');
          ShowMessage(Node.GetElementAttribute('value'));
        end;
        ProcessNode(Node);
        Node := Node.NextSibling;
      end;
    end;
  end;

begin
  ProcessNode(document.Body);
end;

procedure TForm2.Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
  const frame: ICefFrame; httpStatusCode: Integer);
var
  CefStringVisitor: ICefStringVisitor;
begin
  ProcessElementsByName(Chromium1.browser.MainFrame, 'latitude'); // "latitude" = name of field that i want set a value
end;

procedure TForm2.FormShow(Sender: TObject);
begin
  while not(Chromium1.CreateBrowser(CEFWindowParent1, '')) and
    (Chromium1.Initialized) do
  begin
    Sleep(100);
    Application.ProcessMessages;
  end;
  Application.MessageBox('CEFWindowParent1 created!', 'Success', MB_OK + MB_ICONINFORMATION);
  Chromium1.LoadURL('file:///' + ReplaceStr(ExtractFilePath(Application.ExeName) + 'gmaps.html', '\', '/'));
end;

Solution

  • This code is creating the DOM visitor in the browser process but the DOM visitor functions are called in the render process as you can see in the CEF3 code comments

    This would work if you use the "single process" mode but that mode is unsupported by CEF3, it causes errors and you should use that mode for debugging purposes only.

    You need to use several processes. Use the DOMVisitor demo as a template for your app and read all the code comments in that demo too.

    The DOM visitor has to be created in the render process. In order to do that, you send a process message from the browser process to the render process and then you create the TCefDomVisitorOwn subclass inside the event that receives the process message.

    The DOMVisitor demo uses the GlobalCEFApp.OnProcessMessageReceived event to receive the messages in the render process and it creates a TCefFastDomVisitor2 inside that event.

    The TCefFastDomVisitor2 constructor has a procedure parameter called "proc" that is executed when the TCefDomVisitorOwn.visit event is triggered.

    Inside those procedures you can search nodes in the DOM and then send the results back to the browser process calling browser.SendProcessMessage(PID_BROWSER, msg)

    The browser will receive those messages in the TChromium.OnProcessMessageReceived event.

    As you know, Delphi can only debug one process. If you need to debug the code executed in the render process you would need to :