嵌入式Chromium:永远不会执行TCefDomVisitorOwn.visit()方法

时间:2018-09-09 18:52:22

标签: delphi chromium-embedded delphi-10-seattle cef4delphi

我正在使用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;

1 个答案:

答案 0 :(得分:1)

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

中所见

如果您使用“ 单进程”模式,但CEF3不支持该模式,则该方法将起作用,并且会导致错误,您应仅将该模式用于调试目的。

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

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

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

TCefFastDomVisitor2构造函数具有一个称为“ proc”的过程参数,该参数在触发TCefDomVisitorOwn.visit事件时执行。

在这些过程中,您可以搜索DOM中的节点,然后将结果发送回调用浏览器的浏览器进程。SendProcessMessage(PID_BROWSER,msg)

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

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

  • 使用“单一过程”模式,但请记住,在最终版本中不应使用此模式。
  • 使用Delphi中的“无需调试即可运行...”选项,然后选择渲染过程。