Chromium Embedded Framework (CEF)

Fikret Hasovic, October 2018

Overview

The Chromium Embedded Framework (CEF) is an open source framework for embedding a web browser engine based on the Chromium core. It allows developers to add web browser control and implement an HTML5-based layout GUI in a desktop application or to provide web browser capabilities to a software application or game, and provides the infrastructure for developers to add HTML rendering and JavaScript to a C++ project. It also comes with bindings for C, C++, Delphi, Go, Java, .NET / Mono, Visual Basic 6.0, and Python and runs on Linux, Mac OS X and Windows.

There are two versions of Chromium Embedded Framework: CEF 1 and CEF 3. Development of CEF 2 was abandoned after the appearance of the Chromium Content API.

CEF 1 is a single-process implementation based on the Chromium WebKit API. It is no longer actively developed or supported.

CEF 3 is a multi-process implementation based on the Chromium Content API and has performance similar to Google Chrome. It uses asynchronous messaging to communicate between the main application process and one or more render processes (Blink + V8 JavaScript engine). It supports PPAPI plugins and extensions, both internal (PDF viewer) or externally loadable. The single-process run mode is not supported, but still present; currently it is being used for debugging purposes only.

I will therefore focus on the Lazarus version. In fact, there are two different versions of components, called fpCEF3 and CEF4Delphi.

CEF4Delphi is an open source project created by Salvador Díaz Fau to embed Chromium-based browsers in applications made with Delphi or Lazarus/FPC and is based on DCEF3, made by Henri Gourvest.

The current version of CEF4Delphi uses CEF 3.3497.1829.g004ef91 which includes Chromium 69.0.3497.81.

CEF4Delphi is being more developed than fpCEF3 lately, and is up-to-date with the chromium engine. Both projects are hosted on github, respectively: https://github.com/dliw/fpCEF3, https://github.com/salvadordf/CEF4Delphi.

Installation

Besides the Lazarus package, the CEF framework itself is needed. It is important to use the correct version of the CEF framework for the fpCEF release used. Further specific installation instructions can be found on Github: https://github.com/dliw/fpCEF3.

Warning: You can install fpCEF3 or CEF4Delphi, not both, since the component is named Chromium, the same in both packages. However, you can modify it and make a non-standard install.

Package Installation: FpCEF3

Download & Install fpCEF3

1. First of all, we need to download fpCEF3. You can download the latest version from https://github.com/dliw/fpCEF3.

2. Extract & Copy the folder to the Components directory inside your Lazarus installation (this is usually C:\Lazarus\Components).

After downloading the fpCEF3 package, it can be installed in Lazarus as follows:

CEF4Delphi

Follow the same steps as in fpCEF3 install, only you will install different package name, downloaded from https://github.com/salvadordf/CEF4Delphi (see image), and installed component pallete will look just a bit different:

Download Framework CEF3

You can find CEF3 precompiled binaries for different platforms in following location:

https://opensource.spotify.com/cefbuilds/index.html

Be sure to check the version number or commit messages to look for the appropriate version of CEF to be downloaded for it to work.

1. Download CEF. 2. Download & Install 7-zip. You'll need 7-zip to extract the file above. 3. Now extract the file and keep it that way. We'll only need the Release“ & ”Resources folders from this archive.

Usage

Now let’s create a web browser even quicker than you can install one! All using the people's favorite chromium engine!

FpCEF3 is a great project that will let us use the Chromium engine right in our Lazarus project. It is possible to use it in Windows, Linux and Mac OS platforms. But now, we are using Windows. (You can adopt this tutorial for the other platforms quite easily if you know your way around.)

So, let's make this browser …

The previous steps were just preparations – which you'll only have to do once.

Start Lazarus.

Create a new Application project (Project→New Project→Application→OK).

Save the Project (File → Save All) in a directory.

And then copy all the files inside the Release and Resources folders from CEF package, and paste it directly into the project directory.

Drop a new TPanel in to the form and remove it’s Caption. On TPanel drop TEdit and TButton. Set its properties like this:

TEdit.Name = edtURL
TPanel.Align = alTop
TButton.Caption = Go

The result is as follows:

Now we'll have a nice Location bar/Address bar at the top.

Now Drop a new TChromium (from Chromium tab) in the form. Set its properties:

Name = Chromium
Align = alClient

It will fit the Chromium component nicely in the form's blank area.

Now double click the Form1 item in the Object Inspector and enter the code:

1
2
3  procedure TForm1.FormCreate(Sender: TObject);
4  begin
5    edtURL.Text:='https://ibexpert.ne]';
6  end;
7

Now that you are in the Code view, add the necessary unit in the uses clause:

1   uses
2   ..., ..., ...

3 , cef3lib, cef3intf;

Now add the following code on edtURL's OnButtonClick event (select the edtURL component, go to Event tab, click the […] button beside OnButtonClick item):

1 procedure TForm1.edtURLButtonClick(Sender: TObject);
2 begin
3    Chromium.Load(UTF8Decode(edtURL.Text));
4 end;

Now add the following code on Chromium's OnLoadEnd event (select the Chromium component, go to Event tab, click the […] button beside OnLoadEnd item):

1  procedure TForm1.ChromiumLoadEnd(Sender: TObject; const Browser: ICefBrowser;
2     const Frame: ICefFrame; httpStatusCode: Integer);
3 begin
4     edtURL.Text:=UTF8Encode(Browser.MainFrame.Url);
5 end;

All done! In about 2 minutes!

If you want to use CEF4Delphi, you need a little bit more time (I will leave it simple here):

In the project lpr file, add uCEFApplication to uses list. Also, add the following

1
2 	{$IFDEF MSWINDOWS}
3        // CEF3 needs to set the LARGEADDRESSAWARE flag which allows 32-bit 
4           processes to use up to 3GB of RAM.
5       {$SetPEFlags $20}
6   

And make the body of main lpr file look like

1 	GlobalCEFApp := TCefApplication.Create;
2
3   if GlobalCEFApp.StartMainProcess then
4      begin
5            RequireDerivedFormResource:=True;
6            Application.Scaled:=True;
7            Application.Initialize;
8            Application.CreateForm(TForm1, Form1);
9            Application.Run;
10     end;
11
12   GlobalCEFApp.Free;
13   GlobalCEFApp := nil;

In the main unit, add Windows, uCEFChromium, uCEFWindowParent, uCEFInterfaces, uCEFConstants to uses list.

Also, add the following to the implementation section

1  uses
2     uCEFApplication;

In the protected section of your main form class, define:

1   protected
2        // You have to handle this two messages to call
3   NotifyMoveOrResizeStarted or some page elements will be misaligned.
4        procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
5        procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
6        // You also have to handle these two messages to set
7    GlobalCEFApp.OsmodalLoop
8        procedure WMEnterMenuLoop(var aMessage: TMessage); message 
9    WM_ENTERMENULOOP;
10       procedure WMExitMenuLoop(var aMessage: TMessage); message 
11   WM_EXITMENULOOP;
12
13       procedure BrowserCreatedMsg(var aMessage : TMessage); message 
14   CEF_AFTERCREATED;
15       procedure BrowserDestroyMsg(var aMessage : TMessage); message
16   CEF_DESTROY; 

This time we need to use two components, a TChromium and a TCEFWindowParent

And the rest of the code is just pasted here:

  procedure TForm1.BGoClick(Sender: TObject);
 begin
   Chromium1.LoadURL(UTF8Decode(EUrl.Text));
 end;

 procedure TForm1.Chromium1AfterCreated(Sender: TObject;
   const browser: ICefBrowser);
 begin
   // Now the browser is fully initialized we can send a message to the main form to load the initial web page.
   PostMessage(Handle, CEF_AFTERCREATED, 0, 0);
 end;

 procedure TForm1.FormShow(Sender: TObject);
 begin
   // You *MUST* call CreateBrowser to create and initialize the browser.
   // This will trigger the AfterCreated event when the browser is fully
   // initialized and ready to receive commands.

   // GlobalCEFApp.GlobalContextInitialized has to be TRUE before creating any browser
   // If it's not initialized yet, we use a simple timer to create the browser later.
    //if not(Chromium1.CreateBrowser(CEFWindowParent1)) then Timer1.Enabled := True;
   Chromium1.CreateBrowser(CEFWindowParent1);
 end;

 procedure TForm1.WMMove(var aMessage: TWMMove);
 begin
   inherited;

   if (Chromium1 <> nil) then Chromium1.NotifyMoveOrResizeStarted;
 end;

 procedure TForm1.WMMoving(var aMessage: TMessage);
 begin
   inherited;

   if (Chromium1 <> nil) then Chromium1.NotifyMoveOrResizeStarted;
 end;

 procedure TForm1.WMEnterMenuLoop(var aMessage: TMessage);
 begin
   inherited;

   if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then GlobalCEFApp.OsmodalLoop := True;
 end;

 procedure TForm1.WMExitMenuLoop(var aMessage: TMessage);
 begin
   inherited;

   if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then GlobalCEFApp.OsmodalLoop := False;
 end;

 procedure TForm1.BrowserCreatedMsg(var aMessage: TMessage);
 begin
   BGo.Click;
 end;

 procedure TForm1.BrowserDestroyMsg(var aMessage: TMessage);
 begin
   CEFWindowParent1.Free;
 end;

Now Run the project (F9 or Run → Run). Click the “Go” button.