Enumerating DOM nodes in TChromium
Asked Answered
R

3

8

I am trying to enumerate DOM nodes using the following code (under XE2).

I have borrowed most of this from answers given here in SO, but for some reason it's not doing anything.

IOW, ProcessDOM() is not ever getting called.

And, I am at my wits end.

Could someone show me what I am doing wrong here.

Thanks in advance.

procedure ProcessNode(ANode: ICefDomNode);
var
  Node1: ICefDomNode;
begin
  if Assigned(ANode) then begin
    Node1 := ANode.FirstChild;
    while Assigned(Node1) do begin
      {Do stuff with node}
      ProcessNode(Node1);
      Node1 := Node1.NextSibling;
    end;
  end;
end;

procedure ProcessDOM(const ADocument: ICefDomDocument);
begin
  ProcessNode(ADocument.Body);
end;

procedure TMainForm.Chrome1LoadEnd(Sender: TObject; const ABrowser: ICefABrowser; const AFrame: ICefAFrame; AStatus: Integer);
begin
  if Assigned(AFrame) then AFrame.VisitDomProc(ProcessDOM);
end;
Redding answered 22/1, 2013 at 23:53 Comment(7)
As far as I remember, there was a version of Chromium where DOM visiting didn't work at all. What version are you using ?Lat
I am using 'Delphi Chromium Embedded 3' latest SVN. Do you know which version does work?Redding
Hm, it doesn't seem to work in the current snapshot of Chromium 3 (of course I've fixed ICefABrowser and ICefAFrame typos from here). Your code IMHO should work, but doesn't. DOM visiting worked fine in some of the older versions of the old Chromium (not v3) and as far as I remember the new snapshot of the old Chromium has this broken too. I would suggest to file a bug (if there's not any) or contact author. I'm afraid I can't help you with this more.Lat
I'll report it to see if it improves. And.. you're right 'ICefABrowser' and 'ICefAFrame' should have been 'ICefBrowser' and 'ICefFrame' --a result of last minute (albeit sloppy) editing, on my part, before posting here.Redding
No problem. It happens to everyone from time to time. I'm hoping that Henry will fix this soon as it seems to be the long time problem. Thanks!Lat
@Lat Is it possible to pass a pointer to TCefProcessMessageRef? I would like to pass a record.Disheveled
@user3060326, if you mean how to pass a pointer to the ArgumentList, then maybe, but really maybe, by the SetBinary method. But I've never tried that. Sorry, it's a long time since I left CEF...Lat
E
1

I had the same problem and I used the demo guiclient it comes with dcef3. With the following it works.

type TCustomRenderProcessHandler = class(TCefRenderProcessHandlerOwn)
  protected
    function OnProcessMessageReceived(const browser: ICefBrowser; sourceProcess: TCefProcessId; const message: ICefProcessMessage): Boolean; override;
end;

Chromium1.browser.SendProcessMessage(PID_RENDERER, TCefProcessMessageRef.New('visitdom')); 

function TCustomRenderProcessHandler.OnProcessMessageReceived(browser: ICefBrowser; sourceProcess: TCefProcessId; message: ICefProcessMessage): Boolean;
begin 
  if (message.Name = 'visitdom') then begin
    browser.MainFrame.VisitDomProc(
        procedure(const doc: ICefDomDocument) 
        begin
          ProcessNode(Doc.Body);
        end);
    Result := True;
  end; 
end;

initialization
  CefRenderProcessHandler := TCustomRenderProcessHandler.Create;
Engleman answered 9/3, 2013 at 22:40 Comment(0)
A
0

You need to add a procedure to the handler. procedure ProcessNode(ANode: ICefDomNode);

Read this: 1

Amatory answered 14/4, 2013 at 19:49 Comment(1)
This answer doesn't make sense. It links to this question, and why does it need an extra procedure? Can you explain more please?Grunter
N
0

As this blog point out, The main difficulty when accessing a rendered page's DOM is that you can only do so in the same process as the associated renderer for that page.

You can't access dom from browser thread, you have to do it in renderer thread.

First, Forward a message (like visitdom) from browser process to rendering process

procedure TMainForm.crmLoadEnd(Sender: TObject; const browser: ICefBrowser;
  const frame: ICefFrame; httpStatusCode: Integer);
var
  msg : ICefProcessMessage;
begin
  if IsMain(browser, frame) then
    FLoading := False;

  msg := TCefProcessMessageRef.New('visitdom');
  browser.SendProcessMessage(PID_RENDERER, msg);
end;

Second, create a TCustomRenderProcessHandler to handle the message, send the result back to the browser processs.

function TCustomRenderProcessHandler.OnProcessMessageReceived(
  const browser: ICefBrowser; sourceProcess: TCefProcessId;
  const message: ICefProcessMessage): Boolean;
begin
  Result := False;
  if (message.Name = 'visitdom') then
  begin
      browser.MainFrame.VisitDomProc(
        procedure(const doc: ICefDomDocument)
          function ProcessNode(ANode: ICefDomNode) : String;
          var
            Node: ICefDomNode;
          begin
            Result := 'Not Found';
            if Assigned(ANode) then
            begin
              Node := ANode.FirstChild;
              while Assigned(Node) do
              begin
                if Node.ElementTagName='DIV' then
                begin
                  if Node.GetElementAttribute('class')='tv-panels' then
                  begin
                    Result := 'Found';
                    Exit;
                  end;
                end;
                ProcessNode(Node);
                Node := Node.NextSibling;
              end;
            end;
          end;
        var msg : ICefProcessMessage;
        begin
          msg := TCefProcessMessageRef.New('visitdom');
          msg.ArgumentList.SetString(0, processNode(doc.Body));
          browser.SendProcessMessage(PID_BROWSER, msg);
        end);
      Result := True;
  end;
end;

Third, On browser process, create an handler to process the messenage sent back from render process.

procedure TMainForm.crmProcessMessageReceived(Sender: TObject;
  const browser: ICefBrowser; sourceProcess: TCefProcessId;
  const message: ICefProcessMessage; out Result: Boolean);
begin
  Result := False;
  if (message.Name = 'visitdom') then
  begin
    StatusBar.SimpleText := message.ArgumentList.GetString(0);
    Result := True;
  end;
end;

Be careful, while debuging, placing a breakpoint in rendering process never work. It will never reached there.

Nemesis answered 12/5, 2016 at 3:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.