Chromium Embedded:永远不会执行 TCefDomVisitorOwn.visit() 方法

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

我正在使用 CEF4Delphi 并尝试获取页面的确定 html input 元素,然后使用下面的代码将值设置为相同的值,但碰巧方法 TElementNameVisitor.visit(const document: ICefDomDocument); 永远不会执行。

我该如何解决这个问题?

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;

此代码在浏览器进程中创建 DOM 访问者,但 DOM 访问者函数在渲染过程中被调用,正如您在 CEF3 code comments

如果您使用“单进程”模式,这会起作用,但 CEF3 不支持该模式,它会导致错误,您应该仅出于调试目的使用该模式。

您需要使用多个进程。使用 DOMVisitor demo 作为您应用的模板,并阅读该演示中的所有代码注释。

必须在呈现过程中创建 DOM 访问者。为此,您从浏览器进程向渲染进程发送进程消息,然后在接收进程消息的事件中创建 TCefDomVisitorOwn 子类。

DOMVisitor 演示使用 GlobalCEFApp.OnProcessMessageReceived 事件接收渲染过程中的消息,并在该事件中创建一个 TCefFastDomVisitor2。

TCefFastDomVisitor2 构造函数有一个名为 "proc" 的过程参数,它在触发 TCefDomVisitorOwn.visit 事件时执行。

在这些过程中,您可以在 DOM 中搜索节点,然后将结果发送回调用 browser.SendProcessMessage(PID_BROWSER, msg)

的浏览器进程

浏览器将在 TChromium.OnProcessMessageReceived 事件中接收这些消息。

如您所知,Delphi只能调试一个进程。如果您需要调试在渲染过程中执行的代码,您需要:

  • 使用 "single process" 模式,但请记住,您不应在最终版本中使用此模式。
  • 在 Delphi 和 select 渲染过程中使用 "Run Without Debugging..." 选项。