Enumerating DOM nodes in TChromium

☆樱花仙子☆ 提交于 2019-12-30 08:24:20

问题


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;

回答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;



回答2:


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

Read this: 1




回答3:


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.



来源:https://stackoverflow.com/questions/14470176/enumerating-dom-nodes-in-tchromium

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!