正如这篇博客所指出的,访问渲染页面的 DOM 时的主要困难是您只能在与该页面的关联渲染器相同的进程中这样做。
您不能从浏览器线程访问 dom,您必须在渲染器线程中进行。
首先,将消息(如访问域)从浏览器进程转发到渲染进程
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;
其次,创建一个 TCustomRenderProcessHandler 来处理消息,将结果发送回浏览器进程。
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;
第三,在浏览器进程上,创建一个处理程序来处理从渲染进程发回的消息。
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;
请注意,在调试时,在渲染过程中放置断点永远不会起作用。它永远不会到达那里。