2

I need to get the source HTML from a page loaded in TChromium, but i need to store the source inside a variable of another class. In other words, the callback function needs to be inside another class and i can't do it because this exception :

E2009 Incompatible types: 'regular procedure and method pointer'

Here is my code. It works only if 'StringVisitor' function is outside of 'Form1' class.

Any ideas ?

unit simple1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, cefvcl, ceflib;

type
  TForm1 = class(TForm)
    Chromium1: TChromium;
    procedure FormCreate(Sender: TObject);
    procedure Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame;
      httpStatusCode: Integer);
  public
  mySource : string;
  procedure StringVisitor(const str: ustring);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame;
  httpStatusCode: Integer);
begin
  Chromium1.Browser.MainFrame.GetSourceProc(StringVisitor);  // error on this line
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
chromium1.load('http://www.google.com');
end;

procedure TForm1.StringVisitor(const str: ustring);
begin
mySource := str;
end;

end.
delphirules
  • 6,443
  • 17
  • 59
  • 108
  • I'm not sure what signature the routine passed to GetSourceProc is supposed to have, but the traditional (i.e. before anonymous methods and "reference to procedure" syntax) way to do this is write a stand-alone procedure, MyProcedure, to pass to GetSourceProc, and inside MyProcedure simply call Form1.StringVisitor. Form1 must exist at the time, of course. – MartynA Dec 23 '15 at 20:25
  • The error is clear, stringvisitor must be a regular procedure, not a method. You will have to use the global form1 variable to access your form objects... – whosrdaddy Dec 24 '15 at 09:02
  • I've just downloaded the latest version of Chromium (https://github.com/hgourvest/dcef3) and tested you example. I compiles just fine in Delphi Xe6. – Jens Borrisholt Dec 24 '15 at 14:52
  • @JensBorrisholt I'm using Delphi 2007, probably something changed in XE6 that allows do what i'm trying. – delphirules Dec 24 '15 at 15:28
  • @whosrdaddy Can you provide some example code ? If i declared the StringVisitor globally, all instances of TForm1 will share the same procedure, and i can't store the result inside each TForm1 instance... – delphirules Dec 24 '15 at 15:30

1 Answers1

2

Ok Using Delphi 7 I was able to reproduce the problem.

After digging into the problem I found out that there are two diffrnet ways of getting the source of the loaded page:

1) procedure GetSourceProc(const proc: TCefStringVisitorProc); as you are using. The problem with that solution is in pre Dlephi 2009 it only accepts a stand-alone procedure, which gives you problems when you have multible instances of you object in which you want to keep you HTML.

2) procedure GetSource(const visitor: ICefStringVisitor);

Let's start by looking at the expected interface

  ICefStringVisitor = interface(ICefBase)
    ['{63ED4D6C-2FC8-4537-964B-B84C008F6158}']
    procedure Visit(const str: ustring);
  end;

So I started looking into that. Aftter digging in unit ceflib; I found TCefStringVisitorOwn and that is the key to your solution.

The interface of TCefStringVisitorOwn is quite simple:

  TCefStringVisitorOwn = class(TCefBaseOwn, ICefStringVisitor)
  protected
    procedure Visit(const str: ustring); virtual;
  public
    constructor Create; virtual;
  end;

So with this in hand I created a descendant of TCefStringVisitorOwn:

unit SourceContainerU;

interface

uses
  ceflib;

type
  ISourceContainer = interface(ICefStringVisitor)
    function Source: ustring;
  end;

  TSourceContainer = class(TCefStringVisitorOwn, ISourceContainer)
  private
    FSource: ustring;
  protected
    procedure Visit(const str: ustring); override;
  public
    function Source: ustring;
  end;

implementation

{ TSourceContainer }

function TSourceContainer.Source: ustring;
begin
  Result := FSource;
end;

procedure TSourceContainer.Visit(const str: ustring);
begin
  FSource := str;
end;

end.

Nothing magic here.

Only thing left is to create a demo project using this class. In my example I create TChromium at runtime. The form contains a button and a Memo and then this code:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,
  cefvcl, ceflib, SourceContainerU;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    Chromium1: TChromium;
    SourceContainer : ISourceContainer;
    procedure Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame;  httpStatusCode: Integer);
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.Chromium1LoadEnd(Sender: TObject;  const browser: ICefBrowser; const frame: ICefFrame;  httpStatusCode: Integer);
begin
  if Frame = nil then
    exit;

  SourceContainer := TSourceContainer.Create;
  Frame.GetSource(SourceContainer);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chromium1 := TChromium.Create(self);
  Chromium1.Parent := self;
  Chromium1.OnLoadEnd := Chromium1LoadEnd;
  Chromium1.load('http://www.google.com');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(SourceContainer) then
    Memo1.Lines.Text := SourceContainer.Source;
end;

end.

This solution has been tested osing the latest version of Chromium, and I've tested it in Delphi 7, Delphi XE and Delphi XE6. I do not own any Delphi 2007 installation, but I belive it should work in wotk in that aswell.

Jens Borrisholt
  • 6,174
  • 1
  • 33
  • 67