Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reliably know (programmatically), when a page is loaded in Chromium

I have a problem using the OnLoadEnd event of a TChromium (DCEF1).

I have a form with a TButton and a TChromium.

The OnClick event of the button calls a function which lists the forms of the loaded page. If I wait the page finish loading and then click the button, this function works OK; but if I call this function from the TChromium OnLoadEnd event handler the callback function is never called and thus, I get a empty list.

Button code (read the comments into the code):

procedure TForm2.Button3Click(Sender: TObject);
var
  Q: TWebChromium;
begin
  Q := TWebChromium.Create(Chromium1); // <- class to access DOM
  Q.WebFormNames; // <- method to get forms name
  ShowMessage(Q.Forms.Text); // <- show forms
end;

OnLoadEnd code:

procedure TForm2.Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
  const frame: ICefFrame; httpStatusCode: Integer; out Result: Boolean);
begin
  if (browser <> nil) and (browser.GetWindowHandle = TChromium(Sender).BrowserHandle) and ((frame = nil) or (frame.IsMain)) then
  begin
    Button3Click(nil);
  end;
end;

Method code to obtain forms name (read the comments into the code):

procedure TWebChromium.WebFormNames;
var
  Finish: Boolean;
  EndTime: TTime;
begin
  FForms.Clear; // <- property (TStringList)
  if not Assigned(FWebBrowser) then // <- FWebBrowser: property that contain the TChromium
    raise Exception.Create('WebBrowser not assigned');
  if not (FWebBrowser is TChromium) then 
    raise Exception.Create('The WebBrowser property is not a TChromium.');

  Finish := False;
  TChromium(FWebBrowser).Browser.MainFrame.VisitDomProc(
        procedure (const doc: ICefDomDocument) // <- this procedure is not called if this method is called from OnLoadEnd event
        begin
          FForms.CommaText := GetFormsName(doc.Body); 
          Finish := True;
        end
  );
  EndTime := IncSecond(Time, 4);

  repeat Application.ProcessMessages until Finish or (Time > EndTime);
  if Time > EndTime then
    raise Exception.Create('Time out');
end;
like image 529
cadetill Avatar asked Dec 14 '12 08:12

cadetill


1 Answers

Well, i know that this question was asked years ago, but i'm pretty new to TChromium. Here's my solution raised from previous proposals. In general, TChromium sends Event OnLoadEnd, but does so before loading, for example, JS. So I solve the problem so that I wait for some time in the OnLoadEnd procedure in case any scripts still load, and then send a notification, like this

procedure TForm1.OnLoadEndCust(Sender: TObject; const browser: ICefBrowser;
  const frame: ICefFrame; httpStatusCode: Integer);
var EndTime: TTime;
begin
  EndTime := IncSecond(Now, 2);

  repeat Application.ProcessMessages until (Now > EndTime);

end;

So far - that's enough, but is there any other, better or more ellegant solution?

And why we should use VisitDomProc in code above? When we are calling it from OnLoadEnd it seems to be unnecessary. What d'you think guys?

like image 82
kwadratens Avatar answered Oct 19 '22 18:10

kwadratens