0

我正在使用CEF4Delphi并尝试获取input页面的确定 html 元素,然后使用下面的代码将值设置为相同,但碰巧该方法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;
4

1 回答 1

3

此代码在浏览器进程中创建 DOM 访问者,但在渲染进程中调用 DOM 访问者函数,如您在CEF3 代码注释中所见

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

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

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

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

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

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

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

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

  • 使用“单进程”模式,但请记住,您不应在最终版本中使用此模式。
  • 使用 Delphi 中的“Run without Debugging...”选项并选择渲染进程。
于 2018-09-09T21:16:23.337 回答