1
0
mirror of https://github.com/salvadordf/CEF4Delphi.git synced 2025-05-23 21:50:21 +02:00

Update to CEF 91.1.21

The TabbedBrowser2 demo for Windows can now open new tabs without losing the POST data.
This commit is contained in:
Salvador Díaz Fau 2021-06-20 13:08:37 +02:00
parent 371c056192
commit b0259524c5
23 changed files with 1796 additions and 1331 deletions

View File

@ -3,15 +3,15 @@ CEF4Delphi is an open source project created by Salvador Díaz Fau to embed Chro
CEF4Delphi is based on DCEF3 and fpCEF3. The original license of those projects still applies to CEF4Delphi. Read the license terms in the first lines of any *.pas file. CEF4Delphi is based on DCEF3 and fpCEF3. The original license of those projects still applies to CEF4Delphi. Read the license terms in the first lines of any *.pas file.
CEF4Delphi uses CEF 91.1.20 which includes Chromium 91.0.4472.101. CEF4Delphi uses CEF 91.1.21 which includes Chromium 91.0.4472.114.
The CEF binaries used by CEF4Delphi are available for download at spotify : The CEF binaries used by CEF4Delphi are available for download at spotify :
* [Windows 32 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_windows32.tar.bz2) * [Windows 32 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_windows32.tar.bz2)
* [Windows 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_windows64.tar.bz2) * [Windows 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_windows64.tar.bz2)
* [Linux x86 32 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_linux32.tar.bz2) * [Linux x86 32 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_linux32.tar.bz2)
* [Linux x86 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_linux64.tar.bz2) * [Linux x86 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_linux64.tar.bz2)
* [Linux ARM 32 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_linuxarm.tar.bz2) * [Linux ARM 32 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_linuxarm.tar.bz2)
* [Linux ARM 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_linuxarm64.tar.bz2) * [Linux ARM 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_linuxarm64.tar.bz2)
* [MacOS x86 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.20%2Bg5800665%2Bchromium-91.0.4472.101_macosx64.tar.bz2) * [MacOS x86 64 bits](https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_macosx64.tar.bz2)
CEF4Delphi was developed and tested on Delphi 10.4.2 and it has been tested in Delphi 7, Delphi XE, Delphi 10, Delphi 10.2, Delphi 10.3 and Lazarus 2.0.12/FPC 3.2.0. CEF4Delphi includes VCL, FireMonkey (FMX) and Lazarus components. CEF4Delphi was developed and tested on Delphi 10.4.2 and it has been tested in Delphi 7, Delphi XE, Delphi 10, Delphi 10.2, Delphi 10.3 and Lazarus 2.0.12/FPC 3.2.0. CEF4Delphi includes VCL, FireMonkey (FMX) and Lazarus components.

View File

@ -55,9 +55,11 @@ uses
{$R *.res} {$R *.res}
// CEF needs to set the LARGEADDRESSAWARE flag which allows 32-bit processes to use up to 3GB of RAM. {$IFDEF WIN32}
// If you don't add this flag the rederer process will crash when you try to load large images. // CEF needs to set the LARGEADDRESSAWARE flag which allows 32-bit processes to use up to 3GB of RAM.
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} // If you don't add this flag the rederer process will crash when you try to load large images.
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}
{$ENDIF}
begin begin
CreateGlobalCEFApp; CreateGlobalCEFApp;

View File

@ -6,7 +6,7 @@
<MainSource>TabbedBrowser2.dpr</MainSource> <MainSource>TabbedBrowser2.dpr</MainSource>
<Base>True</Base> <Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config> <Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform> <Platform Condition="'$(Platform)'==''">Win64</Platform>
<TargetedPlatforms>3</TargetedPlatforms> <TargetedPlatforms>3</TargetedPlatforms>
<AppType>Application</AppType> <AppType>Application</AppType>
</PropertyGroup> </PropertyGroup>

View File

@ -30,7 +30,6 @@ object BrowserFrame: TBrowserFrame
Top = 5 Top = 5
Width = 25 Width = 25
Height = 25 Height = 25
Align = alLeft
Caption = '3' Caption = '3'
Font.Charset = SYMBOL_CHARSET Font.Charset = SYMBOL_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
@ -76,7 +75,6 @@ object BrowserFrame: TBrowserFrame
Top = 5 Top = 5
Width = 25 Width = 25
Height = 25 Height = 25
Align = alRight
Caption = '=' Caption = '='
Font.Charset = SYMBOL_CHARSET Font.Charset = SYMBOL_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
@ -95,15 +93,16 @@ object BrowserFrame: TBrowserFrame
Height = 35 Height = 35
Align = alClient Align = alClient
BevelOuter = bvNone BevelOuter = bvNone
Padding.Top = 7
Padding.Bottom = 10
TabOrder = 1 TabOrder = 1
DesignSize = (
774
35)
object URLCbx: TComboBox object URLCbx: TComboBox
Left = 0 Left = 2
Top = 7 Top = 7
Width = 774 Width = 770
Height = 21 Height = 21
Align = alClient Anchors = [akLeft, akTop, akRight]
ItemIndex = 0 ItemIndex = 0
TabOrder = 0 TabOrder = 0
Text = 'https://www.google.com' Text = 'https://www.google.com'
@ -173,17 +172,12 @@ object BrowserFrame: TBrowserFrame
Height = 35 Height = 35
Align = alRight Align = alRight
BevelOuter = bvNone BevelOuter = bvNone
Padding.Left = 5
Padding.Top = 5
Padding.Right = 5
Padding.Bottom = 5
TabOrder = 2 TabOrder = 2
object GoBtn: TButton object GoBtn: TButton
Left = 5 Left = 5
Top = 5 Top = 5
Width = 25 Width = 25
Height = 25 Height = 25
Align = alClient
Caption = #9658 Caption = #9658
Font.Charset = ANSI_CHARSET Font.Charset = ANSI_CHARSET
Font.Color = clWindowText Font.Color = clWindowText

View File

@ -45,15 +45,22 @@ uses
{$IFDEF DELPHI16_UP} {$IFDEF DELPHI16_UP}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.StdCtrls, System.SyncObjs,
{$ELSE} {$ELSE}
Windows, Messages, SysUtils, Variants, Windows, Messages, SysUtils, Variants,
Classes, Graphics, Controls, Forms, Dialogs, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, ComCtrls, StdCtrls, ExtCtrls, ComCtrls, StdCtrls, SyncObjs,
{$ENDIF} {$ENDIF}
uCEFWinControl, uCEFWindowParent, uCEFChromiumCore, uCEFChromium, uCEFWinControl, uCEFWindowParent, uCEFChromiumCore, uCEFChromium,
uCEFInterfaces, uCEFTypes, uCEFConstants; uCEFInterfaces, uCEFTypes, uCEFConstants;
const
CEF_UPDATECAPTION = WM_APP + $A55;
CEF_UPDATEADDRESS = WM_APP + $A56;
CEF_UPDATESTATE = WM_APP + $A57;
CEF_UPDATESTATUSTEXT = WM_APP + $A58;
type type
TBrowserTitleEvent = procedure(Sender: TObject; const aTitle : string) of object; TBrowserTitleEvent = procedure(Sender: TObject; const aTitle : string) of object;
@ -90,24 +97,59 @@ type
procedure GoBtnClick(Sender: TObject); procedure GoBtnClick(Sender: TObject);
protected protected
FCriticalSection : TCriticalSection;
FClosing : boolean; // Indicates that this frame is destroying the browser FClosing : boolean; // Indicates that this frame is destroying the browser
FHomepage : string; FHomepage : string;
FPendingAddress : string;
FPendingTitle : string;
FPendingStatus : string;
FPendingIsLoading : boolean;
FPendingCanGoBack : boolean;
FPendingCanGoForward : boolean;
FOnBrowserDestroyed : TNotifyEvent; FOnBrowserDestroyed : TNotifyEvent;
FOnBrowserTitleChange : TBrowserTitleEvent; FOnBrowserTitleChange : TBrowserTitleEvent;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean; function GetInitialized : boolean;
function GetPendingAddress : string;
function GetPendingTitle : string;
function GetPendingStatus : string;
function GetPendingIsLoading : boolean;
function GetPendingCanGoBack : boolean;
function GetPendingCanGoForward : boolean;
procedure SetPendingAddress(const aValue : string);
procedure SetPendingTitle(const aValue : string);
procedure SetPendingStatus(const aValue : string);
procedure SetPendingIsLoading(aValue : boolean);
procedure SetPendingCanGoBack(aValue : boolean);
procedure SetPendingCanGoForward(aValue : boolean);
procedure BrowserCreatedMsg(var aMessage : TMessage); message CEF_AFTERCREATED; procedure BrowserCreatedMsg(var aMessage : TMessage); message CEF_AFTERCREATED;
procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY; procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY;
procedure BrowserUpdateCaptionMsg(var aMessage : TMessage); message CEF_UPDATECAPTION;
procedure BrowserUpdateAddressMsg(var aMessage : TMessage); message CEF_UPDATEADDRESS;
procedure BrowserUpdateStateMsg(var aMessage : TMessage); message CEF_UPDATESTATE;
procedure BrowserUpdateStatusTextMsg(var aMessage : TMessage); message CEF_UPDATESTATUSTEXT;
property PendingAddress : string read GetPendingAddress write SetPendingAddress;
property PendingTitle : string read GetPendingTitle write SetPendingTitle;
property PendingStatus : string read GetPendingStatus write SetPendingStatus;
property PendingIsLoading : boolean read GetPendingIsLoading write SetPendingIsLoading;
property PendingCanGoBack : boolean read GetPendingCanGoBack write SetPendingCanGoBack;
property PendingCanGoForward : boolean read GetPendingCanGoForward write SetPendingCanGoForward;
public public
constructor Create(AOwner : TComponent); override; constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure NotifyMoveOrResizeStarted; procedure NotifyMoveOrResizeStarted;
procedure CreateAllHandles;
procedure CreateBrowser; procedure CreateBrowser;
procedure CloseBrowser; procedure CloseBrowser;
procedure ShowBrowser; procedure ShowBrowser;
procedure HideBrowser; procedure HideBrowser;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean;
property Initialized : boolean read GetInitialized;
property Closing : boolean read FClosing; property Closing : boolean read FClosing;
property Homepage : string read FHomepage write FHomepage; property Homepage : string read FHomepage write FHomepage;
property OnBrowserDestroyed : TNotifyEvent read FOnBrowserDestroyed write FOnBrowserDestroyed; property OnBrowserDestroyed : TNotifyEvent read FOnBrowserDestroyed write FOnBrowserDestroyed;
@ -119,18 +161,137 @@ implementation
{$R *.dfm} {$R *.dfm}
uses uses
uBrowserTab; uCEFMiscFunctions, uBrowserTab;
// The TChromium events are executed in a CEF thread and we should only update the
// GUI controls in the main application thread.
// This demo saves all the information in those events using a synchronization
// object and sends a custom message to update the GUI in the main application thread.
// Destruction steps
// =================
// 1. TBrowserFrame.CloseBrowser sets CanClose to FALSE calls TChromium.CloseBrowser
// which triggers the TChromium.OnClose event.
// 2. TChromium.OnClose sends a CEFBROWSER_DESTROY message to destroy CEFWindowParent1
// in the main thread, which triggers the TChromium.OnBeforeClose event.
// 3. TChromium.OnBeforeClose triggers the TBrowserFrame.OnBrowserDestroyed event
// which sends a CEF_DESTROYTAB message with the TabID to the main form.
constructor TBrowserFrame.Create(AOwner : TComponent); constructor TBrowserFrame.Create(AOwner : TComponent);
begin begin
inherited Create(AOwner); inherited Create(AOwner);
FCriticalSection := TCriticalSection.Create;
FClosing := False; FClosing := False;
FHomepage := ''; FHomepage := '';
FOnBrowserDestroyed := nil; FOnBrowserDestroyed := nil;
FOnBrowserTitleChange := nil; FOnBrowserTitleChange := nil;
end; end;
destructor TBrowserFrame.Destroy;
begin
FreeAndNil(FCriticalSection);
inherited Destroy;
end;
procedure TBrowserFrame.CreateAllHandles;
begin
CreateHandle;
CEFWindowParent1.CreateHandle;
end;
function TBrowserFrame.GetInitialized : boolean;
begin
Result := Chromium1.Initialized;
end;
function TBrowserFrame.GetPendingAddress : string;
begin
FCriticalSection.Acquire;
Result := FPendingAddress;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingTitle : string;
begin
FCriticalSection.Acquire;
Result := FPendingTitle;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingStatus : string;
begin
FCriticalSection.Acquire;
Result := FPendingStatus;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingIsLoading : boolean;
begin
FCriticalSection.Acquire;
Result := FPendingIsLoading;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingCanGoBack : boolean;
begin
FCriticalSection.Acquire;
Result := FPendingCanGoBack;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingCanGoForward : boolean;
begin
FCriticalSection.Acquire;
Result := FPendingCanGoForward;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingAddress(const aValue : string);
begin
FCriticalSection.Acquire;
FPendingAddress := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingTitle(const aValue : string);
begin
FCriticalSection.Acquire;
FPendingTitle := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingStatus(const aValue : string);
begin
FCriticalSection.Acquire;
FPendingStatus := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingIsLoading(aValue : boolean);
begin
FCriticalSection.Acquire;
FPendingIsLoading := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingCanGoBack(aValue : boolean);
begin
FCriticalSection.Acquire;
FPendingCanGoBack := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingCanGoForward(aValue : boolean);
begin
FCriticalSection.Acquire;
FPendingCanGoForward := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.NotifyMoveOrResizeStarted; procedure TBrowserFrame.NotifyMoveOrResizeStarted;
begin begin
Chromium1.NotifyMoveOrResizeStarted; Chromium1.NotifyMoveOrResizeStarted;
@ -201,9 +362,8 @@ procedure TBrowserFrame.Chromium1AddressChange( Sender : TObject;
const frame : ICefFrame; const frame : ICefFrame;
const url : ustring); const url : ustring);
begin begin
if (URLCbx.Items.IndexOf(url) < 0) then URLCbx.Items.Add(url); PendingAddress := url;
PostMessage(Handle, CEF_UPDATEADDRESS, 0, 0);
URLCbx.Text := url;
end; end;
procedure TBrowserFrame.Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser); procedure TBrowserFrame.Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
@ -226,15 +386,9 @@ procedure TBrowserFrame.Chromium1BeforePopup( Sender : TObject;
var noJavascriptAccess : Boolean; var noJavascriptAccess : Boolean;
var Result : Boolean); var Result : Boolean);
begin begin
case targetDisposition of Result := not(assigned(Parent) and
WOD_NEW_FOREGROUND_TAB, (Parent is TBrowserTab) and
WOD_NEW_BACKGROUND_TAB, TBrowserTab(Parent).DoOnBeforePopup(windowInfo, client, targetFrameName, popupFeatures, targetDisposition));
WOD_NEW_WINDOW : Result := True; // For simplicity, this demo blocks new tabs and new windows.
WOD_NEW_POPUP : Result := not(CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures));
else Result := False;
end;
end; end;
procedure TBrowserFrame.Chromium1OpenUrlFromTab( Sender : TObject; procedure TBrowserFrame.Chromium1OpenUrlFromTab( Sender : TObject;
@ -245,7 +399,9 @@ procedure TBrowserFrame.Chromium1OpenUrlFromTab( Sender : TObjec
userGesture : Boolean; userGesture : Boolean;
out Result : Boolean); out Result : Boolean);
begin begin
Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB, WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]); Result := assigned(Parent) and
(Parent is TBrowserTab) and
TBrowserTab(Parent).DoOpenUrlFromTab(targetUrl, targetDisposition);
end; end;
procedure TBrowserFrame.Chromium1Close( Sender : TObject; procedure TBrowserFrame.Chromium1Close( Sender : TObject;
@ -281,38 +437,32 @@ procedure TBrowserFrame.Chromium1LoadingStateChange( Sender : TObject
canGoBack : Boolean; canGoBack : Boolean;
canGoForward : Boolean); canGoForward : Boolean);
begin begin
BackBtn.Enabled := canGoBack; PendingIsLoading := isLoading;
ForwardBtn.Enabled := canGoForward; PendingCanGoBack := canGoBack;
PendingCanGoForward := canGoForward;
if isLoading then PostMessage(Handle, CEF_UPDATESTATE, 0, 0);
begin
ReloadBtn.Enabled := False;
StopBtn.Enabled := True;
end
else
begin
ReloadBtn.Enabled := True;
StopBtn.Enabled := False;
end;
end; end;
procedure TBrowserFrame.Chromium1StatusMessage( Sender : TObject; procedure TBrowserFrame.Chromium1StatusMessage( Sender : TObject;
const browser : ICefBrowser; const browser : ICefBrowser;
const value : ustring); const value : ustring);
begin begin
StatusBar1.Panels[0].Text := value; PendingStatus := value;
PostMessage(Handle, CEF_UPDATESTATUSTEXT, 0, 0);
end; end;
procedure TBrowserFrame.Chromium1TitleChange( Sender : TObject; procedure TBrowserFrame.Chromium1TitleChange( Sender : TObject;
const browser : ICefBrowser; const browser : ICefBrowser;
const title : ustring); const title : ustring);
begin begin
if not(assigned(FOnBrowserTitleChange)) then exit;
if (length(title) > 0) then if (length(title) > 0) then
FOnBrowserTitleChange(self, title) PendingTitle := title
else else
FOnBrowserTitleChange(self, Chromium1.DocumentURL); PendingTitle := Chromium1.DocumentURL;
PostMessage(Handle, CEF_UPDATECAPTION, 0, 0);
end; end;
procedure TBrowserFrame.BrowserCreatedMsg(var aMessage : TMessage); procedure TBrowserFrame.BrowserCreatedMsg(var aMessage : TMessage);
@ -326,14 +476,63 @@ begin
CEFWindowParent1.Free; CEFWindowParent1.Free;
end; end;
procedure TBrowserFrame.BrowserUpdateCaptionMsg(var aMessage : TMessage);
begin
if assigned(FOnBrowserTitleChange) then
FOnBrowserTitleChange(self, PendingTitle);
end;
procedure TBrowserFrame.BrowserUpdateAddressMsg(var aMessage : TMessage);
var
TempAddress : string;
begin
TempAddress := PendingAddress;
if (URLCbx.Items.IndexOf(TempAddress) < 0) then
URLCbx.Items.Add(TempAddress);
URLCbx.Text := TempAddress;
end;
procedure TBrowserFrame.BrowserUpdateStateMsg(var aMessage : TMessage);
begin
BackBtn.Enabled := PendingCanGoBack;
ForwardBtn.Enabled := PendingCanGoForward;
if PendingIsLoading then
begin
ReloadBtn.Enabled := False;
StopBtn.Enabled := True;
end
else
begin
ReloadBtn.Enabled := True;
StopBtn.Enabled := False;
end;
end;
procedure TBrowserFrame.BrowserUpdateStatusTextMsg(var aMessage : TMessage);
begin
StatusBar1.Panels[0].Text := PendingStatus;
end;
function TBrowserFrame.CreateClientHandler(var windowInfo : TCefWindowInfo; function TBrowserFrame.CreateClientHandler(var windowInfo : TCefWindowInfo;
var client : ICefClient; var client : ICefClient;
const targetFrameName : string; const targetFrameName : string;
const popupFeatures : TCefPopupFeatures) : boolean; const popupFeatures : TCefPopupFeatures) : boolean;
var
TempRect : TRect;
begin begin
Result := assigned(Parent) and if CEFWindowParent1.HandleAllocated and
(Parent is TBrowserTab) and Chromium1.CreateClientHandler(client, False) then
TBrowserTab(Parent).CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures); begin
Result := True;
TempRect := CEFWindowParent1.ClientRect;
WindowInfoAsChild(windowInfo, CEFWindowParent1.Handle, TempRect, '');
end
else
Result := False;
end; end;
end. end.

View File

@ -44,10 +44,10 @@ interface
uses uses
{$IFDEF DELPHI16_UP} {$IFDEF DELPHI16_UP}
Winapi.Windows, System.Classes, Winapi.Messages, Vcl.ComCtrls, Vcl.Controls, Winapi.Windows, System.Classes, Winapi.Messages, Vcl.ComCtrls, Vcl.Controls,
Vcl.Forms, Vcl.Forms, System.SysUtils,
{$ELSE} {$ELSE}
Windows, Classes, Messages, ComCtrls, Controls, Windows, Classes, Messages, ComCtrls, Controls,
Forms, Forms, SysUtils,
{$ENDIF} {$ENDIF}
uCEFInterfaces, uCEFTypes, uBrowserFrame; uCEFInterfaces, uCEFTypes, uBrowserFrame;
@ -58,6 +58,8 @@ type
FTabID : cardinal; FTabID : cardinal;
function GetParentForm : TCustomForm; function GetParentForm : TCustomForm;
function GetInitialized : boolean;
function GetClosing : boolean;
function PostFormMessage(aMsg : cardinal; aWParam : WPARAM = 0; aLParam : LPARAM = 0) : boolean; function PostFormMessage(aMsg : cardinal; aWParam : WPARAM = 0; aLParam : LPARAM = 0) : boolean;
@ -69,13 +71,18 @@ type
public public
constructor Create(AOwner: TComponent; aTabID : cardinal; const aCaption : string); reintroduce; constructor Create(AOwner: TComponent; aTabID : cardinal; const aCaption : string); reintroduce;
procedure NotifyMoveOrResizeStarted; procedure NotifyMoveOrResizeStarted;
procedure CreateFrame(const aHomepage : string = '');
procedure CreateBrowser(const aHomepage : string); procedure CreateBrowser(const aHomepage : string);
procedure CloseBrowser; procedure CloseBrowser;
procedure ShowBrowser; procedure ShowBrowser;
procedure HideBrowser; procedure HideBrowser;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean; function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean;
function DoOnBeforePopup(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures; targetDisposition : TCefWindowOpenDisposition) : boolean;
function DoOpenUrlFromTab(const targetUrl : string; targetDisposition : TCefWindowOpenDisposition) : boolean;
property TabID : cardinal read FTabID; property TabID : cardinal read FTabID;
property Closing : boolean read GetClosing;
property Initialized : boolean read GetInitialized;
end; end;
implementation implementation
@ -107,6 +114,18 @@ begin
Result := nil; Result := nil;
end; end;
function TBrowserTab.GetInitialized : boolean;
begin
Result := (FBrowserFrame <> nil) and
FBrowserFrame.Initialized;
end;
function TBrowserTab.GetClosing : boolean;
begin
Result := (FBrowserFrame <> nil) and
FBrowserFrame.Closing;
end;
function TBrowserTab.PostFormMessage(aMsg : cardinal; aWParam : WPARAM; aLParam : LPARAM) : boolean; function TBrowserTab.PostFormMessage(aMsg : cardinal; aWParam : WPARAM; aLParam : LPARAM) : boolean;
var var
TempForm : TCustomForm; TempForm : TCustomForm;
@ -122,17 +141,28 @@ begin
FBrowserFrame.NotifyMoveOrResizeStarted; FBrowserFrame.NotifyMoveOrResizeStarted;
end; end;
procedure TBrowserTab.CreateBrowser(const aHomepage : string); procedure TBrowserTab.CreateFrame(const aHomepage : string);
begin begin
if (FBrowserFrame = nil) then
begin
FBrowserFrame := TBrowserFrame.Create(self); FBrowserFrame := TBrowserFrame.Create(self);
FBrowserFrame.Name := 'BrowserFrame' + IntToStr(TabID);
FBrowserFrame.Parent := self; FBrowserFrame.Parent := self;
FBrowserFrame.Align := alClient; FBrowserFrame.Align := alClient;
FBrowserFrame.Visible := True; FBrowserFrame.Visible := True;
FBrowserFrame.Homepage := aHomepage;
FBrowserFrame.OnBrowserDestroyed := BrowserFrame_OnBrowserDestroyed; FBrowserFrame.OnBrowserDestroyed := BrowserFrame_OnBrowserDestroyed;
FBrowserFrame.OnBrowserTitleChange := BrowserFrame_OnBrowserTitleChange; FBrowserFrame.OnBrowserTitleChange := BrowserFrame_OnBrowserTitleChange;
FBrowserFrame.CreateAllHandles;
end;
FBrowserFrame.CreateBrowser; FBrowserFrame.Homepage := aHomepage;
end;
procedure TBrowserTab.CreateBrowser(const aHomepage : string);
begin
CreateFrame(aHomepage);
if (FBrowserFrame <> nil) then FBrowserFrame.CreateBrowser;
end; end;
procedure TBrowserTab.CloseBrowser; procedure TBrowserTab.CloseBrowser;
@ -166,13 +196,34 @@ function TBrowserTab.CreateClientHandler(var windowInfo : TCefWindowInfo;
var client : ICefClient; var client : ICefClient;
const targetFrameName : string; const targetFrameName : string;
const popupFeatures : TCefPopupFeatures) : boolean; const popupFeatures : TCefPopupFeatures) : boolean;
begin
Result := (FBrowserFrame <> nil) and
FBrowserFrame.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures);
end;
function TBrowserTab.DoOnBeforePopup(var windowInfo : TCefWindowInfo;
var client : ICefClient;
const targetFrameName : string;
const popupFeatures : TCefPopupFeatures;
targetDisposition : TCefWindowOpenDisposition) : boolean;
var var
TempForm : TCustomForm; TempForm : TCustomForm;
begin begin
TempForm := ParentForm; TempForm := ParentForm;
Result := (TempForm <> nil) and Result := (TempForm <> nil) and
(TempForm is TMainForm) and (TempForm is TMainForm) and
TMainForm(TempForm).CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures); TMainForm(TempForm).DoOnBeforePopup(windowInfo, client, targetFrameName, popupFeatures, targetDisposition);
end;
function TBrowserTab.DoOpenUrlFromTab(const targetUrl : string;
targetDisposition : TCefWindowOpenDisposition) : boolean;
var
TempForm : TCustomForm;
begin
TempForm := ParentForm;
Result := (TempForm <> nil) and
(TempForm is TMainForm) and
TMainForm(TempForm).DoOpenUrlFromTab(targetUrl, targetDisposition);
end; end;
end. end.

View File

@ -29,8 +29,10 @@ object ChildForm: TChildForm
object Chromium1: TChromium object Chromium1: TChromium
OnTitleChange = Chromium1TitleChange OnTitleChange = Chromium1TitleChange
OnBeforePopup = Chromium1BeforePopup OnBeforePopup = Chromium1BeforePopup
OnAfterCreated = Chromium1AfterCreated
OnBeforeClose = Chromium1BeforeClose OnBeforeClose = Chromium1BeforeClose
OnClose = Chromium1Close OnClose = Chromium1Close
OnOpenUrlFromTab = Chromium1OpenUrlFromTab
Left = 24 Left = 24
Top = 56 Top = 56
end end

View File

@ -53,6 +53,9 @@ uses
uCEFChromium, uCEFTypes, uCEFInterfaces, uCEFConstants, uCEFWindowParent, uCEFWinControl, uCEFChromium, uCEFTypes, uCEFInterfaces, uCEFConstants, uCEFWindowParent, uCEFWinControl,
uCEFChromiumCore; uCEFChromiumCore;
const
CEF_UPDATECAPTION = WM_APP + $A55;
type type
TChildForm = class(TForm) TChildForm = class(TForm)
Chromium1: TChromium; Chromium1: TChromium;
@ -63,29 +66,37 @@ type
procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
procedure Chromium1BeforePopup(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; const popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo; var client: ICefClient; var settings: TCefBrowserSettings; var extra_info: ICefDictionaryValue; var noJavascriptAccess: Boolean; var Result: Boolean); procedure Chromium1BeforePopup(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; const popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo; var client: ICefClient; var settings: TCefBrowserSettings; var extra_info: ICefDictionaryValue; var noJavascriptAccess: Boolean; var Result: Boolean);
procedure Chromium1TitleChange(Sender: TObject; const browser: ICefBrowser; const title: ustring); procedure Chromium1TitleChange(Sender: TObject; const browser: ICefBrowser; const title: ustring);
procedure Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction); procedure Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction);
procedure Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser); procedure Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
procedure Chromium1OpenUrlFromTab(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; out Result: Boolean);
protected protected
FCriticalSection : TCriticalSection;
FCanClose : boolean; FCanClose : boolean;
FClosing : boolean; FClosing : boolean;
FClientInitialized : boolean; FBrowserWasCreated : boolean;
FTitle : string;
FPopupFeatures : TCefPopupFeatures; FPopupFeatures : TCefPopupFeatures;
function GetInitialized : boolean;
procedure WMMove(var aMessage : TWMMove); message WM_MOVE; procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
procedure WMMoving(var aMessage : TMessage); message WM_MOVING; procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP; procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP; procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY; procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY;
procedure BrowserUpdateCaptionMsg(var aMessage : TMessage); message CEF_UPDATECAPTION;
public public
procedure AfterConstruction; override; procedure AfterConstruction; override;
function CreateBrowser(const aHomepage : string) : boolean;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean; function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean;
procedure ApplyPopupFeatures; procedure ApplyPopupFeatures;
property ClientInitialized : boolean read FClientInitialized; property Initialized : boolean read GetInitialized;
property Closing : boolean read FClosing; property Closing : boolean read FClosing;
end; end;
@ -103,8 +114,10 @@ uses
// Destruction steps // Destruction steps
// ================= // =================
// 1. FormCloseQuery sets CanClose to FALSE calls TChromium.CloseBrowser which triggers the TChromium.OnClose event. // 1. FormCloseQuery sets CanClose to FALSE calls TChromium.CloseBrowser which
// 2. TChromium.OnClose sends a CEFBROWSER_DESTROY message to destroy CEFWindowParent1 in the main thread, which triggers the TChromium.OnBeforeClose event. // triggers the TChromium.OnClose event.
// 2. TChromium.OnClose sends a CEFBROWSER_DESTROY message to destroy CEFWindowParent1
// in the main thread, which triggers the TChromium.OnBeforeClose event.
// 3. TChromium.OnBeforeClose sets FCanClose := True and sends WM_CLOSE to the form. // 3. TChromium.OnBeforeClose sets FCanClose := True and sends WM_CLOSE to the form.
procedure TChildForm.AfterConstruction; procedure TChildForm.AfterConstruction;
@ -127,7 +140,6 @@ begin
Chromium1.CreateClientHandler(client, False) then Chromium1.CreateClientHandler(client, False) then
begin begin
Result := True; Result := True;
FClientInitialized := True;
FPopupFeatures := popupFeatures; FPopupFeatures := popupFeatures;
TempRect := CEFWindowParent1.ClientRect; TempRect := CEFWindowParent1.ClientRect;
@ -140,6 +152,12 @@ begin
Result := False; Result := False;
end; end;
function TChildForm.CreateBrowser(const aHomepage : string) : boolean;
begin
Chromium1.DefaultURL := aHomepage;
Result := Chromium1.CreateBrowser(CEFWindowParent1);
end;
procedure TChildForm.ApplyPopupFeatures; procedure TChildForm.ApplyPopupFeatures;
begin begin
if (FPopupFeatures.xset <> 0) then Chromium1.SetFormLeftTo(FPopupFeatures.x); if (FPopupFeatures.xset <> 0) then Chromium1.SetFormLeftTo(FPopupFeatures.x);
@ -148,13 +166,18 @@ begin
if (FPopupFeatures.heightset <> 0) then Chromium1.ResizeFormHeightTo(FPopupFeatures.height); if (FPopupFeatures.heightset <> 0) then Chromium1.ResizeFormHeightTo(FPopupFeatures.height);
end; end;
procedure TChildForm.Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
begin
FBrowserWasCreated := True;
end;
procedure TChildForm.Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser); procedure TChildForm.Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
begin begin
FCanClose := True; FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0); PostMessage(Handle, WM_CLOSE, 0, 0);
end; end;
procedure TChildForm.Chromium1BeforePopup(Sender : TObject; procedure TChildForm.Chromium1BeforePopup( Sender : TObject;
const browser : ICefBrowser; const browser : ICefBrowser;
const frame : ICefFrame; const frame : ICefFrame;
const targetUrl : ustring; const targetUrl : ustring;
@ -169,15 +192,18 @@ procedure TChildForm.Chromium1BeforePopup(Sender : TObject;
var noJavascriptAccess : Boolean; var noJavascriptAccess : Boolean;
var Result : Boolean); var Result : Boolean);
begin begin
case targetDisposition of Result := not(TMainForm(Owner).DoOnBeforePopup(windowInfo, client, targetFrameName, popupFeatures, targetDisposition));
WOD_NEW_FOREGROUND_TAB, end;
WOD_NEW_BACKGROUND_TAB,
WOD_NEW_WINDOW : Result := True; // For simplicity, this demo blocks new tabs and new windows.
WOD_NEW_POPUP : Result := not(TMainForm(Owner).CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures)); procedure TChildForm.Chromium1OpenUrlFromTab( Sender : TObject;
const browser : ICefBrowser;
else Result := False; const frame : ICefFrame;
end; const targetUrl : ustring;
targetDisposition : TCefWindowOpenDisposition;
userGesture : Boolean;
out Result : Boolean);
begin
Result := not(TMainForm(Owner).DoOpenUrlFromTab(targetUrl, targetDisposition));
end; end;
procedure TChildForm.Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction); procedure TChildForm.Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction);
@ -188,7 +214,18 @@ end;
procedure TChildForm.Chromium1TitleChange(Sender: TObject; const browser: ICefBrowser; const title: ustring); procedure TChildForm.Chromium1TitleChange(Sender: TObject; const browser: ICefBrowser; const title: ustring);
begin begin
Caption := title; try
FCriticalSection.Acquire;
FTitle := title;
finally
FCriticalSection.Release;
PostMessage(Handle, CEF_UPDATECAPTION, 0, 0);
end;
end;
function TChildForm.GetInitialized : boolean;
begin
Result := Chromium1.Initialized;
end; end;
procedure TChildForm.WMMove(var aMessage : TWMMove); procedure TChildForm.WMMove(var aMessage : TWMMove);
@ -226,6 +263,8 @@ end;
procedure TChildForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure TChildForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin begin
if FBrowserWasCreated then
begin
CanClose := FCanClose; CanClose := FCanClose;
if not(FClosing) then if not(FClosing) then
@ -234,18 +273,24 @@ begin
Visible := False; Visible := False;
Chromium1.CloseBrowser(True); Chromium1.CloseBrowser(True);
end; end;
end
else
CanClose := True;
end; end;
procedure TChildForm.FormCreate(Sender: TObject); procedure TChildForm.FormCreate(Sender: TObject);
begin begin
FCriticalSection := TCriticalSection.Create;
FBrowserWasCreated := False;
FCanClose := False; FCanClose := False;
FClosing := False; FClosing := False;
FClientInitialized := False;
end; end;
procedure TChildForm.FormDestroy(Sender: TObject); procedure TChildForm.FormDestroy(Sender: TObject);
begin begin
if FClientInitialized and TMainForm(Owner).HandleAllocated then FCriticalSection.Free;
if FBrowserWasCreated and TMainForm(Owner).HandleAllocated then
PostMessage(TMainForm(Owner).Handle, CEF_CHILDDESTROYED, 0, 0); PostMessage(TMainForm(Owner).Handle, CEF_CHILDDESTROYED, 0, 0);
end; end;
@ -254,4 +299,14 @@ begin
CEFWindowParent1.Free; CEFWindowParent1.Free;
end; end;
procedure TChildForm.BrowserUpdateCaptionMsg(var aMessage : TMessage);
begin
try
FCriticalSection.Acquire;
Caption := FTitle;
finally
FCriticalSection.Release;
end;
end;
end. end.

View File

@ -25,6 +25,7 @@ object MainForm: TMainForm
Height = 703 Height = 703
Align = alClient Align = alClient
TabOrder = 0 TabOrder = 0
TabWidth = 150
end end
object ButtonPnl: TPanel object ButtonPnl: TPanel
Left = 0 Left = 0
@ -39,15 +40,11 @@ object MainForm: TMainForm
Padding.Right = 3 Padding.Right = 3
Padding.Bottom = 3 Padding.Bottom = 3
TabOrder = 1 TabOrder = 1
DesignSize = (
32
703)
object AddTabBtn: TSpeedButton object AddTabBtn: TSpeedButton
Left = 3 Left = 3
Top = 3 Top = 3
Width = 26 Width = 26
Height = 26 Height = 26
Align = alTop
Caption = '+' Caption = '+'
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
@ -56,14 +53,12 @@ object MainForm: TMainForm
Font.Style = [] Font.Style = []
ParentFont = False ParentFont = False
OnClick = AddTabBtnClick OnClick = AddTabBtnClick
ExplicitWidth = 27
end end
object RemoveTabBtn: TSpeedButton object RemoveTabBtn: TSpeedButton
Left = 3 Left = 3
Top = 32 Top = 32
Width = 26 Width = 26
Height = 26 Height = 26
Anchors = [akLeft, akTop, akRight]
Caption = #8722 Caption = #8722
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText

View File

@ -49,13 +49,14 @@ uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, SyncObjs, Windows, Messages, SysUtils, Variants, Classes, Graphics, SyncObjs,
Controls, Forms, Dialogs, ComCtrls, ToolWin, Buttons, ExtCtrls, Controls, Forms, Dialogs, ComCtrls, ToolWin, Buttons, ExtCtrls,
{$ENDIF} {$ENDIF}
uCEFApplication, uCEFInterfaces, uCEFTypes, uCEFConstants, uChildForm; uCEFApplication, uCEFInterfaces, uCEFTypes, uCEFConstants, uChildForm, uBrowserTab;
const const
CEF_INITIALIZED = WM_APP + $A50; CEF_INITIALIZED = WM_APP + $A50;
CEF_DESTROYTAB = WM_APP + $A51; CEF_DESTROYTAB = WM_APP + $A51;
CEF_CREATENEXTCHILD = WM_APP + $A52; CEF_CREATENEXTCHILD = WM_APP + $A52;
CEF_CHILDDESTROYED = WM_APP + $A53; CEF_CREATENEXTTAB = WM_APP + $A53;
CEF_CHILDDESTROYED = WM_APP + $A54;
HOMEPAGE_URL = 'https://www.google.com'; HOMEPAGE_URL = 'https://www.google.com';
DEFAULT_TAB_CAPTION = 'New tab'; DEFAULT_TAB_CAPTION = 'New tab';
@ -76,33 +77,41 @@ type
procedure FormDestroy(Sender: TObject); procedure FormDestroy(Sender: TObject);
protected protected
FHiddenTab : TBrowserTab;
FChildForm : TChildForm; FChildForm : TChildForm;
FCriticalSection : TCriticalSection; FCriticalSection : TCriticalSection;
FCanClose : boolean; FCanClose : boolean;
FClosing : boolean; // Set to True in the CloseQuery event. FClosing : boolean; // Set to True in the CloseQuery event.
FLastTabID : cardinal; // Used by NextTabID to generate unique tab IDs FLastTabID : cardinal; // Used by NextTabID to generate unique tab IDs
FPendingURL : string;
function GetNextTabID : cardinal; function GetNextTabID : cardinal;
function GetPopupChildCount : integer; function GetPopupChildCount : integer;
function GetBrowserTabCount : integer;
procedure EnableButtonPnl; procedure EnableButtonPnl;
function CloseAllBrowsers : boolean; function CloseAllBrowsers : boolean;
procedure CloseTab(aIndex : integer); procedure CloseTab(aIndex : integer);
procedure CreateHiddenBrowsers;
procedure CEFInitializedMsg(var aMessage : TMessage); message CEF_INITIALIZED; procedure CEFInitializedMsg(var aMessage : TMessage); message CEF_INITIALIZED;
procedure DestroyTabMsg(var aMessage : TMessage); message CEF_DESTROYTAB; procedure DestroyTabMsg(var aMessage : TMessage); message CEF_DESTROYTAB;
procedure CreateNextChildMsg(var aMessage : TMessage); message CEF_CREATENEXTCHILD; procedure CreateNextChildMsg(var aMessage : TMessage); message CEF_CREATENEXTCHILD;
procedure CreateNextTabMsg(var aMessage : TMessage); message CEF_CREATENEXTTAB;
procedure ChildDestroyedMsg(var aMessage : TMessage); message CEF_CHILDDESTROYED; procedure ChildDestroyedMsg(var aMessage : TMessage); message CEF_CHILDDESTROYED;
procedure WMMove(var aMessage : TWMMove); message WM_MOVE; procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
procedure WMMoving(var aMessage : TMessage); message WM_MOVING; procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP; procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP; procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
procedure WMQueryEndSession(var aMessage: TWMQueryEndSession); message WM_QUERYENDSESSION;
property NextTabID : cardinal read GetNextTabID; property NextTabID : cardinal read GetNextTabID;
property PopupChildCount : integer read GetPopupChildCount; property PopupChildCount : integer read GetPopupChildCount;
property BrowserTabCount : integer read GetBrowserTabCount;
public public
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean; function DoOnBeforePopup(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures; targetDisposition : TCefWindowOpenDisposition) : boolean;
function DoOpenUrlFromTab(const targetUrl : string; targetDisposition : TCefWindowOpenDisposition) : boolean;
end; end;
var var
@ -114,8 +123,6 @@ implementation
{$R *.dfm} {$R *.dfm}
uses
uBrowserTab;
// This demo shows how to use a TPageControl with TFrames that include // This demo shows how to use a TPageControl with TFrames that include
// CEF4Delphi browsers. // CEF4Delphi browsers.
@ -146,6 +153,24 @@ uses
// the PopupBrowser2 demo. Please, read the code comments in that demo for all // the PopupBrowser2 demo. Please, read the code comments in that demo for all
// details about handling the custom child forms. // details about handling the custom child forms.
// Additionally, this demo also creates new tabs when a browser triggers the
// TChromium.OnBeforePopup event.
// VCL components *MUST* be created and destroyed in the main thread but CEF
// executes the TChromium.OnBeforePopup in a different thread.
// For this reason this demo creates a hidden popup form (TChildForm) and a
// hidden TBrowserTab in case CEF needs to show a popup window.
// TChromium.OnBeforePopup calls TMainForm.DoOnBeforePopup to handle all the
// events in the same place.
// TMainForm.DoOnBeforePopup will call CreateClientHandler to initialize some
// parameters and create the new ICefClient using the hidden form or tab.
// After that, it sends a custom message to show the popup form or tab and create
// a new one.
// To close safely this demo you must close all the browser tabs first following // To close safely this demo you must close all the browser tabs first following
// this steps : // this steps :
// //
@ -167,7 +192,12 @@ procedure CreateGlobalCEFApp;
begin begin
GlobalCEFApp := TCefApplication.Create; GlobalCEFApp := TCefApplication.Create;
GlobalCEFApp.cache := 'cache'; GlobalCEFApp.cache := 'cache';
GlobalCEFApp.EnablePrintPreview := True;
GlobalCEFApp.OnContextInitialized := GlobalCEFApp_OnContextInitialized; GlobalCEFApp.OnContextInitialized := GlobalCEFApp_OnContextInitialized;
// This is a workaround for the CEF4Delphi issue #324 :
// https://github.com/salvadordf/CEF4Delphi/issues/324
GlobalCEFApp.DisableFeatures := 'WinUseBrowserSpellChecker';
end; end;
procedure TMainForm.EnableButtonPnl; procedure TMainForm.EnableButtonPnl;
@ -177,7 +207,7 @@ begin
ButtonPnl.Enabled := True; ButtonPnl.Enabled := True;
Caption := 'Tabbed Browser 2'; Caption := 'Tabbed Browser 2';
cursor := crDefault; cursor := crDefault;
if (BrowserPageCtrl.PageCount = 0) then AddTabBtn.Click; if (BrowserTabCount = 0) then AddTabBtn.Click;
end; end;
end; end;
@ -197,12 +227,28 @@ begin
while (i >= 0) do while (i >= 0) do
begin begin
TempForm := screen.CustomForms[i];
// Only count the fully initialized child forms and not the one waiting to be used. // Only count the fully initialized child forms and not the one waiting to be used.
TempForm := screen.CustomForms[i];
if (TempForm is TChildForm) and if (TempForm is TChildForm) and
TChildForm(TempForm).ClientInitialized then TChildForm(TempForm).Initialized then
inc(Result);
dec(i);
end;
end;
function TMainForm.GetBrowserTabCount : integer;
var
i : integer;
begin
Result := 0;
i := pred(BrowserPageCtrl.PageCount);
while (i >= 0) do
begin
// Only count the fully initialized browser tabs and not the one waiting to be used.
if TBrowserTab(BrowserPageCtrl.Pages[i]).Initialized then
inc(Result); inc(Result);
dec(i); dec(i);
@ -224,9 +270,7 @@ end;
procedure TMainForm.CEFInitializedMsg(var aMessage : TMessage); procedure TMainForm.CEFInitializedMsg(var aMessage : TMessage);
begin begin
EnableButtonPnl; EnableButtonPnl;
CreateHiddenBrowsers;
if (FChildForm = nil) then
TChildForm.Create(self);
end; end;
procedure TMainForm.DestroyTabMsg(var aMessage : TMessage); procedure TMainForm.DestroyTabMsg(var aMessage : TMessage);
@ -234,6 +278,8 @@ var
i : integer; i : integer;
TempTab : TBrowserTab; TempTab : TBrowserTab;
begin begin
// Every tab sends a CEF_DESTROYTAB message when its browser has been destroyed
// and then we can destroy the TBrowserTab control.
i := 0; i := 0;
while (i < BrowserPageCtrl.PageCount) do while (i < BrowserPageCtrl.PageCount) do
begin begin
@ -248,7 +294,9 @@ begin
inc(i); inc(i);
end; end;
if FClosing and (PopupChildCount = 0) and (BrowserPageCtrl.PageCount = 0) then // Here we check if this was the last initialized browser to close the
// application safely.
if FClosing and (PopupChildCount = 0) and (BrowserTabCount = 0) then
begin begin
FCanClose := True; FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0); PostMessage(Handle, WM_CLOSE, 0, 0);
@ -257,7 +305,10 @@ end;
procedure TMainForm.ChildDestroyedMsg(var aMessage : TMessage); procedure TMainForm.ChildDestroyedMsg(var aMessage : TMessage);
begin begin
if FClosing and (PopupChildCount = 0) and (BrowserPageCtrl.PageCount = 0) then // Every destroyed child form sends a CEF_CHILDDESTROYED message
// Here we check if this was the last initialized browser to close the
// application safely.
if FClosing and (PopupChildCount = 0) and (BrowserTabCount = 0) then
begin begin
FCanClose := True; FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0); PostMessage(Handle, WM_CLOSE, 0, 0);
@ -271,7 +322,12 @@ begin
if (FChildForm <> nil) then if (FChildForm <> nil) then
begin begin
if (aMessage.lParam <> 0) then
FChildForm.CreateBrowser(FPendingURL)
else
FChildForm.ApplyPopupFeatures; FChildForm.ApplyPopupFeatures;
FChildForm.Show; FChildForm.Show;
end; end;
@ -281,6 +337,31 @@ begin
end; end;
end; end;
procedure TMainForm.CreateNextTabMsg(var aMessage : TMessage);
begin
try
FCriticalSection.Acquire;
if (FHiddenTab <> nil) then
begin
FHiddenTab.TabVisible := True;
FHiddenTab.PageIndex := pred(BrowserPageCtrl.PageCount);
if (aMessage.lParam <> 0) then
FHiddenTab.CreateBrowser(FPendingURL);
BrowserPageCtrl.ActivePageIndex := FHiddenTab.PageIndex;
end;
FHiddenTab := TBrowserTab.Create(self, NextTabID, DEFAULT_TAB_CAPTION);
FHiddenTab.PageControl := BrowserPageCtrl;
FHiddenTab.TabVisible := False;
FHiddenTab.CreateFrame;
finally
FCriticalSection.Release;
end;
end;
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin begin
CanClose := FCanClose; CanClose := FCanClose;
@ -304,6 +385,7 @@ begin
FClosing := False; FClosing := False;
FLastTabID := 0; FLastTabID := 0;
FChildForm := nil; FChildForm := nil;
FHiddenTab := nil;
FCriticalSection := TCriticalSection.Create; FCriticalSection := TCriticalSection.Create;
end; end;
@ -317,14 +399,13 @@ begin
if (GlobalCEFApp <> nil) and GlobalCEFApp.GlobalContextInitialized then if (GlobalCEFApp <> nil) and GlobalCEFApp.GlobalContextInitialized then
begin begin
EnableButtonPnl; EnableButtonPnl;
CreateHiddenBrowsers;
if (FChildForm = nil) then
FChildForm := TChildForm.Create(self);
end; end;
end; end;
procedure TMainForm.RemoveTabBtnClick(Sender: TObject); procedure TMainForm.RemoveTabBtnClick(Sender: TObject);
begin begin
// Call TBrowserTab.CloseBrowser in the active tab
CloseTab(BrowserPageCtrl.ActivePageIndex); CloseTab(BrowserPageCtrl.ActivePageIndex);
end; end;
@ -332,16 +413,16 @@ function TMainForm.CloseAllBrowsers : boolean;
var var
i : integer; i : integer;
TempForm : TCustomForm; TempForm : TCustomForm;
TempTab : TBrowserTab;
begin begin
Result := False; Result := False;
i := pred(screen.CustomFormCount); i := pred(screen.CustomFormCount);
while (i >= 0) do while (i >= 0) do
begin begin
TempForm := screen.CustomForms[i]; TempForm := screen.CustomForms[i];
if (TempForm is TChildForm) and if (TempForm is TChildForm) and
TChildForm(TempForm).ClientInitialized and TChildForm(TempForm).Initialized and
not(TChildForm(TempForm).Closing) then not(TChildForm(TempForm).Closing) then
begin begin
PostMessage(TempForm.Handle, WM_CLOSE, 0, 0); PostMessage(TempForm.Handle, WM_CLOSE, 0, 0);
@ -352,11 +433,16 @@ begin
end; end;
i := pred(BrowserPageCtrl.PageCount); i := pred(BrowserPageCtrl.PageCount);
while (i >= 0) do while (i >= 0) do
begin begin
TBrowserTab(BrowserPageCtrl.Pages[i]).CloseBrowser; TempTab := TBrowserTab(BrowserPageCtrl.Pages[i]);
if TempTab.Initialized and not(TempTab.Closing) then
begin
TempTab.CloseBrowser;
Result := True; Result := True;
end;
dec(i); dec(i);
end; end;
end; end;
@ -367,6 +453,26 @@ begin
TBrowserTab(BrowserPageCtrl.Pages[aIndex]).CloseBrowser; TBrowserTab(BrowserPageCtrl.Pages[aIndex]).CloseBrowser;
end; end;
procedure TMainForm.CreateHiddenBrowsers;
begin
try
FCriticalSection.Acquire;
if (FChildForm = nil) then
FChildForm := TChildForm.Create(self);
if (FHiddenTab = nil) then
begin
FHiddenTab := TBrowserTab.Create(self, NextTabID, DEFAULT_TAB_CAPTION);
FHiddenTab.PageControl := BrowserPageCtrl;
FHiddenTab.TabVisible := False;
FHiddenTab.CreateFrame;
end;
finally
FCriticalSection.Release;
end;
end;
procedure TMainForm.WMMove(var aMessage : TWMMove); procedure TMainForm.WMMove(var aMessage : TWMMove);
var var
i : integer; i : integer;
@ -411,17 +517,70 @@ begin
GlobalCEFApp.OsmodalLoop := False; GlobalCEFApp.OsmodalLoop := False;
end; end;
function TMainForm.CreateClientHandler(var windowInfo : TCefWindowInfo; procedure TMainForm.WMQueryEndSession(var aMessage: TWMQueryEndSession);
begin
// We return False (0) to close the browser correctly while we can.
// This is not what Microsoft recommends doing when an application receives
// WM_QUERYENDSESSION but at least we avoid TApplication calling HALT when
// it receives WM_ENDSESSION.
// The CEF subprocesses may receive WM_QUERYENDSESSION and WM_ENDSESSION
// before the main process and they may crash before closing the main form.
aMessage.Result := 0;
PostMessage(Handle, WM_CLOSE, 0, 0);
end;
function TMainForm.DoOnBeforePopup(var windowInfo : TCefWindowInfo;
var client : ICefClient; var client : ICefClient;
const targetFrameName : string; const targetFrameName : string;
const popupFeatures : TCefPopupFeatures) : boolean; const popupFeatures : TCefPopupFeatures;
targetDisposition : TCefWindowOpenDisposition) : boolean;
begin begin
try try
FCriticalSection.Acquire; FCriticalSection.Acquire;
case targetDisposition of
WOD_NEW_FOREGROUND_TAB,
WOD_NEW_BACKGROUND_TAB :
Result := (FHiddenTab <> nil) and
FHiddenTab.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures) and
PostMessage(Handle, CEF_CREATENEXTTAB, 0, ord(False));
WOD_NEW_WINDOW,
WOD_NEW_POPUP :
Result := (FChildForm <> nil) and Result := (FChildForm <> nil) and
FChildForm.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures) and FChildForm.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures) and
PostMessage(Handle, CEF_CREATENEXTCHILD, 0, 0); PostMessage(Handle, CEF_CREATENEXTCHILD, 0, ord(False));
else Result := False;
end;
finally
FCriticalSection.Release;
end;
end;
function TMainForm.DoOpenUrlFromTab(const targetUrl : string;
targetDisposition : TCefWindowOpenDisposition) : boolean;
begin
try
FCriticalSection.Acquire;
case targetDisposition of
WOD_NEW_FOREGROUND_TAB,
WOD_NEW_BACKGROUND_TAB :
begin
FPendingURL := targetUrl;
Result := PostMessage(Handle, CEF_CREATENEXTTAB, 0, ord(True));
end;
WOD_NEW_WINDOW,
WOD_NEW_POPUP :
begin
FPendingURL := targetUrl;
Result := PostMessage(Handle, CEF_CREATENEXTCHILD, 0, ord(True));
end
else Result := False;
end;
finally finally
FCriticalSection.Release; FCriticalSection.Release;
end; end;

View File

@ -1,983 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{2A491C1D-D0F3-4D4B-9606-F7FC09C7713E}</ProjectGuid>
<ProjectVersion>18.8</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>TabbedBrowser2.dpr</MainSource>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
<Cfg_2_Win32>true</Cfg_2_Win32>
<CfgParent>Cfg_2</CfgParent>
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
<DCC_ExeOutput>..\..\..\bin</DCC_ExeOutput>
<DCC_E>false</DCC_E>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
<SanitizedProjectName>TabbedBrowser2</SanitizedProjectName>
<VerInfo_Locale>3082</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_UsePackage>DBXSqliteDriver;bindcompdbx;fmxase;DBXDb2Driver;DBXInterBaseDriver;vcl;DBXSybaseASEDriver;vclactnband;RESTComponents;vclFireDAC;IndyProtocols250;FireDACDb2Driver;IndyCore250;DataSnapFireDAC;svnui;tethering;dsnapcon;FireDACADSDriver;FireDACMSAccDriver;fmxFireDAC;DBXMSSQLDriver;vclimg;FireDACInfxDriver;DatasnapConnectorsFreePascal;FireDAC;FireDACMSSQLDriver;vcltouch;Componentes_UI;vcldb;bindcompfmx;svn;Detours;FireDACSqliteDriver;FireDACPgDriver;DBXOracleDriver;inetdb;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;SVGPackage;soaprtl;DbxCommonDriver;FireDACIBDriver;fmx;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;fmxdae;vclwinx;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;dbexpress;FireDACDBXDriver;vclx;bindcomp;appanalytics;dsnap;DataSnapCommon;DBXInformixDriver;FireDACCommon;bindcompvcl;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;vclie;CEF4Delphi_FMX;bindengine;DBXMySQLDriver;FireDACOracleDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;DBXFirebirdDriver;DataSnapProviderClient;FireDACMongoDBDriver;FireDACCommonODBC;FireDACCommonDriver;CloudService;DataSnapClient;VisualStyles;IndySystem250;inet;DataSnapServerMidas;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<DCC_UsePackage>DBXSqliteDriver;bindcompdbx;fmxase;DBXDb2Driver;DBXInterBaseDriver;vcl;DBXSybaseASEDriver;vclactnband;RESTComponents;vclFireDAC;IndyProtocols250;FireDACDb2Driver;IndyCore250;DataSnapFireDAC;tethering;dsnapcon;FireDACADSDriver;FireDACMSAccDriver;fmxFireDAC;DBXMSSQLDriver;vclimg;FireDACInfxDriver;DatasnapConnectorsFreePascal;FireDAC;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;FireDACSqliteDriver;FireDACPgDriver;DBXOracleDriver;inetdb;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;FireDACIBDriver;fmx;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;fmxdae;vclwinx;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;dbexpress;FireDACDBXDriver;vclx;bindcomp;appanalytics;dsnap;DataSnapCommon;DBXInformixDriver;FireDACCommon;bindcompvcl;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;vclie;CEF4Delphi_FMX;bindengine;DBXMySQLDriver;FireDACOracleDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;DBXFirebirdDriver;DataSnapProviderClient;FireDACMongoDBDriver;FireDACCommonODBC;FireDACCommonDriver;CloudService;DataSnapClient;IndySystem250;inet;DataSnapServerMidas;$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_DebugDCUs>true</DCC_DebugDCUs>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
<DCC_RemoteDebug>true</DCC_RemoteDebug>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<DCC_RemoteDebug>false</DCC_RemoteDebug>
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>0</DCC_DebugInformation>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<DCCReference Include="uMainForm.pas">
<Form>MainForm</Form>
</DCCReference>
<DCCReference Include="uBrowserFrame.pas">
<Form>BrowserFrame</Form>
<DesignClass>TFrame</DesignClass>
</DCCReference>
<DCCReference Include="uBrowserTab.pas"/>
<BuildConfiguration Include="Release">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType>Application</Borland.ProjectType>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">TabbedBrowser2.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclIPIndyImpl260.bpl">IP Abstraction Indy Implementation Design Time</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k260.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp260.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="3">
<DeployFile LocalName="..\..\..\bin\TabbedBrowser2.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>TabbedBrowser2.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidClassesDexFile">
<Platform Name="Android">
<RemoteDir>classes</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>classes</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidFileProvider">
<Platform Name="Android">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidGDBServer">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiFile">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiv7aFile">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeMipsFile">
<Platform Name="Android">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDef">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV21">
<Platform Name="Android">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon144">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon24">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage470">
<Platform Name="Android">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage640">
<Platform Name="Android">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage960">
<Platform Name="Android">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Strings">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyFramework">
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyModule">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.dll;.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="DependencyPackage">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Name="File">
<Platform Name="Android">
<Operation>0</Operation>
</Platform>
<Platform Name="Android64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>0</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1024x768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1668x2388">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048x2732">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2224">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2388x1668">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2732x2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768x1024">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1125">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1136x640">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1242x2688">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1334">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch1792">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2208">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2436">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2688x1242">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch320">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch640">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch640x1136">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch750">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch828">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceResourceRules">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
<Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
</Project>

View File

@ -35,7 +35,7 @@
<PackageName Value="LCL"/> <PackageName Value="LCL"/>
</Item2> </Item2>
</RequiredPackages> </RequiredPackages>
<Units Count="4"> <Units Count="5">
<Unit0> <Unit0>
<Filename Value="TabbedBrowser2.lpr"/> <Filename Value="TabbedBrowser2.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
@ -50,14 +50,17 @@
<Unit2> <Unit2>
<Filename Value="uBrowserFrame.pas"/> <Filename Value="uBrowserFrame.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<ComponentName Value="BrowserFrame"/>
<HasResources Value="True"/> <HasResources Value="True"/>
<ResourceBaseClass Value="Frame"/>
</Unit2> </Unit2>
<Unit3> <Unit3>
<Filename Value="uBrowserTab.pas"/> <Filename Value="uBrowserTab.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
</Unit3> </Unit3>
<Unit4>
<Filename Value="uChildForm.pas"/>
<IsPartOfProject Value="True"/>
<HasResources Value="True"/>
</Unit4>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

View File

@ -42,18 +42,26 @@ program TabbedBrowser2;
{$I cef.inc} {$I cef.inc}
uses uses
{$IFDEF DELPHI16_UP}
Vcl.Forms,
WinApi.Windows,
{$ELSE}
Forms, Forms,
LCLIntf, LCLType, LMessages, Interfaces, LCLIntf, LCLType, LMessages, Interfaces,
{$ENDIF }
uCEFApplication, uCEFApplication,
uMainForm in 'uMainForm.pas' {MainForm}, uMainForm in 'uMainForm.pas' {MainForm},
uBrowserFrame in 'uBrowserFrame.pas' {BrowserFrame: TFrame}, uBrowserFrame in 'uBrowserFrame.pas' {BrowserFrame: TFrame},
uBrowserTab in 'uBrowserTab.pas'; uBrowserTab in 'uBrowserTab.pas',
uChildForm in 'uChildForm.pas' {ChildForm};
{.$R *.res} {.$R *.res}
// CEF needs to set the LARGEADDRESSAWARE flag which allows 32-bit processes to use up to 3GB of RAM. {$IFDEF WIN32}
// If you don't add this flag the rederer process will crash when you try to load large images. // CEF needs to set the LARGEADDRESSAWARE flag which allows 32-bit processes to use up to 3GB of RAM.
{$SetPEFlags $20} // If you don't add this flag the rederer process will crash when you try to load large images.
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}
{$ENDIF}
begin begin
CreateGlobalCEFApp; CreateGlobalCEFApp;
@ -61,6 +69,9 @@ begin
if GlobalCEFApp.StartMainProcess then if GlobalCEFApp.StartMainProcess then
begin begin
Application.Initialize; Application.Initialize;
{$IFDEF DELPHI11_UP}
Application.MainFormOnTaskbar := True;
{$ENDIF}
Application.CreateForm(TMainForm, MainForm); Application.CreateForm(TMainForm, MainForm);
Application.Run; Application.Run;
end; end;

View File

@ -1,116 +1,115 @@
object BrowserFrame: TBrowserFrame object BrowserFrame: TBrowserFrame
Left = 0 Left = 0
Height = 670
Top = 0 Top = 0
Width = 932 Width = 932
ClientHeight = 670 Height = 670
ClientWidth = 932
TabOrder = 0 TabOrder = 0
DesignLeft = 269
DesignTop = 169
object NavControlPnl: TPanel object NavControlPnl: TPanel
Left = 0 Left = 0
Height = 28
Top = 0 Top = 0
Width = 932 Width = 932
Height = 35
Align = alTop Align = alTop
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 28
ClientWidth = 932
Enabled = False Enabled = False
TabOrder = 0 TabOrder = 0
object NavButtonPnl: TPanel object NavButtonPnl: TPanel
Left = 0 Left = 0
Height = 28
Top = 0 Top = 0
Width = 123 Width = 123
Height = 35
Align = alLeft Align = alLeft
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 28
ClientWidth = 123
TabOrder = 0 TabOrder = 0
object BackBtn: TButton object BackBtn: TButton
Left = 0 Left = 3
Height = 25 Top = 3
Top = 0
Width = 25 Width = 25
Height = 25
Caption = '3' Caption = '3'
Font.CharSet = SYMBOL_CHARSET Font.Charset = SYMBOL_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -19 Font.Height = -19
Font.Name = 'Webdings' Font.Name = 'Webdings'
OnClick = BackBtnClick Font.Style = []
ParentFont = False ParentFont = False
TabOrder = 0 TabOrder = 0
OnClick = BackBtnClick
end end
object ForwardBtn: TButton object ForwardBtn: TButton
Left = 30 Left = 33
Height = 25 Top = 3
Top = 0
Width = 25 Width = 25
Height = 25
Caption = '4' Caption = '4'
Font.CharSet = SYMBOL_CHARSET Font.Charset = SYMBOL_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -19 Font.Height = -19
Font.Name = 'Webdings' Font.Name = 'Webdings'
OnClick = ForwardBtnClick Font.Style = []
ParentFont = False ParentFont = False
TabOrder = 1 TabOrder = 1
OnClick = ForwardBtnClick
end end
object ReloadBtn: TButton object ReloadBtn: TButton
Left = 59 Left = 62
Height = 25 Top = 3
Top = 0
Width = 25 Width = 25
Height = 25
Caption = 'q' Caption = 'q'
Font.CharSet = SYMBOL_CHARSET Font.Charset = SYMBOL_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -19 Font.Height = -19
Font.Name = 'Webdings' Font.Name = 'Webdings'
OnClick = ReloadBtnClick Font.Style = []
ParentFont = False ParentFont = False
TabOrder = 2 TabOrder = 2
OnClick = ReloadBtnClick
end end
object StopBtn: TButton object StopBtn: TButton
Left = 88 Left = 91
Height = 25 Top = 3
Top = 0
Width = 25 Width = 25
Height = 25
Caption = '=' Caption = '='
Font.CharSet = SYMBOL_CHARSET Font.Charset = SYMBOL_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -19 Font.Height = -19
Font.Name = 'Webdings' Font.Name = 'Webdings'
OnClick = StopBtnClick Font.Style = []
ParentFont = False ParentFont = False
TabOrder = 3 TabOrder = 3
OnClick = StopBtnClick
end end
end end
object URLEditPnl: TPanel object URLEditPnl: TPanel
Left = 123 Left = 121
Height = 28
Top = 0 Top = 0
Width = 774 Width = 774
Height = 35
Align = alClient Align = alClient
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 28
ClientWidth = 774
TabOrder = 1 TabOrder = 1
object URLCbx: TComboBox object URLCbx: TComboBox
Left = 0 Left = 0
Height = 23 Top = 5
Top = 1 Width = 770
Width = 774 Height = 21
Anchors = [akTop, akLeft, akRight] Anchors = [akLeft, akTop, akRight]
ItemHeight = 15
ItemIndex = 0 ItemIndex = 0
TabOrder = 0
Text = 'https://www.google.com'
Items.Strings = ( Items.Strings = (
'https://www.google.com' 'https://www.google.com'
'https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending'
'https://www.whatismybrowser.com/detect/what-http-headers-is-my-b' +
'rowser-sending'
'https://www.w3schools.com/js/tryit.asp?filename=tryjs_win_close' 'https://www.w3schools.com/js/tryit.asp?filename=tryjs_win_close'
'https://www.w3schools.com/js/tryit.asp?filename=tryjs_alert' 'https://www.w3schools.com/js/tryit.asp?filename=tryjs_alert'
'https://www.w3schools.com/js/tryit.asp?filename=tryjs_loc_assign' 'https://www.w3schools.com/js/tryit.asp?filename=tryjs_loc_assign'
'https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_style_backgroundcolor'
'https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_styl' +
'e_backgroundcolor'
'https://www.w3schools.com/html/html5_video.asp' 'https://www.w3schools.com/html/html5_video.asp'
'http://www.adobe.com/software/flash/about/' 'http://www.adobe.com/software/flash/about/'
'http://isflashinstalled.com/' 'http://isflashinstalled.com/'
@ -118,7 +117,9 @@ object BrowserFrame: TBrowserFrame
'https://www.ultrasounds.com/' 'https://www.ultrasounds.com/'
'https://www.whatismybrowser.com/detect/is-flash-installed' 'https://www.whatismybrowser.com/detect/is-flash-installed'
'http://html5test.com/' 'http://html5test.com/'
'https://webrtc.github.io/samples/src/content/devices/input-output/'
'https://webrtc.github.io/samples/src/content/devices/input-outpu' +
't/'
'https://test.webrtc.org/' 'https://test.webrtc.org/'
'https://www.w3schools.com/' 'https://www.w3schools.com/'
'http://webglsamples.org/' 'http://webglsamples.org/'
@ -126,9 +127,15 @@ object BrowserFrame: TBrowserFrame
'https://www.briskbard.com' 'https://www.briskbard.com'
'https://www.youtube.com' 'https://www.youtube.com'
'https://html5demos.com/drag/' 'https://html5demos.com/drag/'
'https://developers.google.com/maps/documentation/javascript/examples/streetview-embed?hl=fr'
'https://www.w3schools.com/Tags/tryit.asp?filename=tryhtml_iframe_name' 'https://developers.google.com/maps/documentation/javascript/exam' +
'http://www-db.deis.unibo.it/courses/TW/DOCS/w3schools/html/tryit.asp-filename=tryhtml5_html_manifest.html' 'ples/streetview-embed?hl=fr'
'https://www.w3schools.com/Tags/tryit.asp?filename=tryhtml_iframe' +
'_name'
'http://www-db.deis.unibo.it/courses/TW/DOCS/w3schools/html/tryit' +
'.asp-filename=tryhtml5_html_manifest.html'
'https://www.browserleaks.com/webrtc' 'https://www.browserleaks.com/webrtc'
'https://frames-per-second.appspot.com/' 'https://frames-per-second.appspot.com/'
'chrome://version/' 'chrome://version/'
@ -148,44 +155,39 @@ object BrowserFrame: TBrowserFrame
'chrome://gpucrash' 'chrome://gpucrash'
'chrome://gpuhang' 'chrome://gpuhang'
'chrome://extensions-support' 'chrome://extensions-support'
'chrome://process-internals' 'chrome://process-internals')
)
TabOrder = 0
Text = 'https://www.google.com'
end end
end end
object ConfigPnl: TPanel object ConfigPnl: TPanel
Left = 897 Left = 895
Height = 28
Top = 0 Top = 0
Width = 35 Width = 35
Height = 35
Align = alRight Align = alRight
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 28
ClientWidth = 35
TabOrder = 2 TabOrder = 2
object GoBtn: TButton object GoBtn: TButton
Left = 5 Left = 3
Height = 25 Top = 3
Top = 0
Width = 25 Width = 25
Height = 25
Caption = '►' Caption = '►'
Font.CharSet = ANSI_CHARSET Font.Charset = ANSI_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -17 Font.Height = -17
Font.Name = 'Arial' Font.Name = 'Arial'
Font.Style = [fsBold] Font.Style = [fsBold]
OnClick = GoBtnClick
ParentFont = False ParentFont = False
TabOrder = 0 TabOrder = 0
OnClick = GoBtnClick
end end
end end
end end
object StatusBar1: TStatusBar object StatusBar1: TStatusBar
Left = 0 Left = 0
Height = 23 Top = 651
Top = 647
Width = 932 Width = 932
Height = 19
Panels = < Panels = <
item item
Width = 500 Width = 500
@ -194,9 +196,9 @@ object BrowserFrame: TBrowserFrame
end end
object CEFWindowParent1: TCEFWindowParent object CEFWindowParent1: TCEFWindowParent
Left = 0 Left = 0
Height = 619 Top = 35
Top = 28
Width = 932 Width = 932
Height = 616
Align = alClient Align = alClient
TabOrder = 2 TabOrder = 2
end end
@ -211,7 +213,7 @@ object BrowserFrame: TBrowserFrame
OnBeforeClose = Chromium1BeforeClose OnBeforeClose = Chromium1BeforeClose
OnClose = Chromium1Close OnClose = Chromium1Close
OnOpenUrlFromTab = Chromium1OpenUrlFromTab OnOpenUrlFromTab = Chromium1OpenUrlFromTab
left = 40 Left = 40
top = 72 Top = 72
end end
end end

View File

@ -44,12 +44,25 @@ unit uBrowserFrame;
interface interface
uses uses
{$IFDEF DELPHI16_UP}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.StdCtrls, System.SyncObjs,
{$ELSE}
LCLIntf, LCLType, LMessages, Messages, SysUtils, Variants, LCLIntf, LCLType, LMessages, Messages, SysUtils, Variants,
Classes, Graphics, Controls, Forms, Dialogs, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, ComCtrls, StdCtrls, ExtCtrls, ComCtrls, StdCtrls, SyncObjs,
{$ENDIF}
uCEFWinControl, uCEFWindowParent, uCEFChromiumCore, uCEFChromium, uCEFWinControl, uCEFWindowParent, uCEFChromiumCore, uCEFChromium,
uCEFInterfaces, uCEFTypes, uCEFConstants; uCEFInterfaces, uCEFTypes, uCEFConstants;
const
CEF_UPDATECAPTION = WM_APP + $A55;
CEF_UPDATEADDRESS = WM_APP + $A56;
CEF_UPDATESTATE = WM_APP + $A57;
CEF_UPDATESTATUSTEXT = WM_APP + $A58;
type type
TBrowserTitleEvent = procedure(Sender: TObject; const aTitle : string) of object; TBrowserTitleEvent = procedure(Sender: TObject; const aTitle : string) of object;
@ -86,20 +99,59 @@ type
procedure GoBtnClick(Sender: TObject); procedure GoBtnClick(Sender: TObject);
protected protected
FCriticalSection : TCriticalSection;
FClosing : boolean; // Indicates that this frame is destroying the browser FClosing : boolean; // Indicates that this frame is destroying the browser
FHomepage : string; FHomepage : string;
FPendingAddress : string;
FPendingTitle : string;
FPendingStatus : string;
FPendingIsLoading : boolean;
FPendingCanGoBack : boolean;
FPendingCanGoForward : boolean;
FOnBrowserDestroyed : TNotifyEvent; FOnBrowserDestroyed : TNotifyEvent;
FOnBrowserTitleChange : TBrowserTitleEvent; FOnBrowserTitleChange : TBrowserTitleEvent;
function GetInitialized : boolean;
function GetPendingAddress : string;
function GetPendingTitle : string;
function GetPendingStatus : string;
function GetPendingIsLoading : boolean;
function GetPendingCanGoBack : boolean;
function GetPendingCanGoForward : boolean;
procedure SetPendingAddress(const aValue : string);
procedure SetPendingTitle(const aValue : string);
procedure SetPendingStatus(const aValue : string);
procedure SetPendingIsLoading(aValue : boolean);
procedure SetPendingCanGoBack(aValue : boolean);
procedure SetPendingCanGoForward(aValue : boolean);
procedure BrowserCreatedMsg(var aMessage : TMessage); message CEF_AFTERCREATED; procedure BrowserCreatedMsg(var aMessage : TMessage); message CEF_AFTERCREATED;
procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY; procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY;
procedure BrowserUpdateCaptionMsg(var aMessage : TMessage); message CEF_UPDATECAPTION;
procedure BrowserUpdateAddressMsg(var aMessage : TMessage); message CEF_UPDATEADDRESS;
procedure BrowserUpdateStateMsg(var aMessage : TMessage); message CEF_UPDATESTATE;
procedure BrowserUpdateStatusTextMsg(var aMessage : TMessage); message CEF_UPDATESTATUSTEXT;
property PendingAddress : string read GetPendingAddress write SetPendingAddress;
property PendingTitle : string read GetPendingTitle write SetPendingTitle;
property PendingStatus : string read GetPendingStatus write SetPendingStatus;
property PendingIsLoading : boolean read GetPendingIsLoading write SetPendingIsLoading;
property PendingCanGoBack : boolean read GetPendingCanGoBack write SetPendingCanGoBack;
property PendingCanGoForward : boolean read GetPendingCanGoForward write SetPendingCanGoForward;
public public
constructor Create(AOwner : TComponent); override; constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure NotifyMoveOrResizeStarted; procedure NotifyMoveOrResizeStarted;
procedure CreateAllHandles;
procedure CreateBrowser; procedure CreateBrowser;
procedure CloseBrowser; procedure CloseBrowser;
procedure ShowBrowser;
procedure HideBrowser;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean;
property Initialized : boolean read GetInitialized;
property Closing : boolean read FClosing; property Closing : boolean read FClosing;
property Homepage : string read FHomepage write FHomepage; property Homepage : string read FHomepage write FHomepage;
property OnBrowserDestroyed : TNotifyEvent read FOnBrowserDestroyed write FOnBrowserDestroyed; property OnBrowserDestroyed : TNotifyEvent read FOnBrowserDestroyed write FOnBrowserDestroyed;
@ -110,16 +162,138 @@ implementation
{$R *.lfm} {$R *.lfm}
uses
uCEFMiscFunctions, uBrowserTab;
// The TChromium events are executed in a CEF thread and we should only update the
// GUI controls in the main application thread.
// This demo saves all the information in those events using a synchronization
// object and sends a custom message to update the GUI in the main application thread.
// Destruction steps
// =================
// 1. TBrowserFrame.CloseBrowser sets CanClose to FALSE calls TChromium.CloseBrowser
// which triggers the TChromium.OnClose event.
// 2. TChromium.OnClose sends a CEFBROWSER_DESTROY message to destroy CEFWindowParent1
// in the main thread, which triggers the TChromium.OnBeforeClose event.
// 3. TChromium.OnBeforeClose triggers the TBrowserFrame.OnBrowserDestroyed event
// which sends a CEF_DESTROYTAB message with the TabID to the main form.
constructor TBrowserFrame.Create(AOwner : TComponent); constructor TBrowserFrame.Create(AOwner : TComponent);
begin begin
inherited Create(AOwner); inherited Create(AOwner);
FCriticalSection := TCriticalSection.Create;
FClosing := False; FClosing := False;
FHomepage := ''; FHomepage := '';
FOnBrowserDestroyed := nil; FOnBrowserDestroyed := nil;
FOnBrowserTitleChange := nil; FOnBrowserTitleChange := nil;
end; end;
destructor TBrowserFrame.Destroy;
begin
FreeAndNil(FCriticalSection);
inherited Destroy;
end;
procedure TBrowserFrame.CreateAllHandles;
begin
CreateHandle;
CEFWindowParent1.CreateHandle;
end;
function TBrowserFrame.GetInitialized : boolean;
begin
Result := Chromium1.Initialized;
end;
function TBrowserFrame.GetPendingAddress : string;
begin
FCriticalSection.Acquire;
Result := FPendingAddress;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingTitle : string;
begin
FCriticalSection.Acquire;
Result := FPendingTitle;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingStatus : string;
begin
FCriticalSection.Acquire;
Result := FPendingStatus;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingIsLoading : boolean;
begin
FCriticalSection.Acquire;
Result := FPendingIsLoading;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingCanGoBack : boolean;
begin
FCriticalSection.Acquire;
Result := FPendingCanGoBack;
FCriticalSection.Release;
end;
function TBrowserFrame.GetPendingCanGoForward : boolean;
begin
FCriticalSection.Acquire;
Result := FPendingCanGoForward;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingAddress(const aValue : string);
begin
FCriticalSection.Acquire;
FPendingAddress := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingTitle(const aValue : string);
begin
FCriticalSection.Acquire;
FPendingTitle := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingStatus(const aValue : string);
begin
FCriticalSection.Acquire;
FPendingStatus := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingIsLoading(aValue : boolean);
begin
FCriticalSection.Acquire;
FPendingIsLoading := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingCanGoBack(aValue : boolean);
begin
FCriticalSection.Acquire;
FPendingCanGoBack := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.SetPendingCanGoForward(aValue : boolean);
begin
FCriticalSection.Acquire;
FPendingCanGoForward := aValue;
FCriticalSection.Release;
end;
procedure TBrowserFrame.NotifyMoveOrResizeStarted; procedure TBrowserFrame.NotifyMoveOrResizeStarted;
begin begin
Chromium1.NotifyMoveOrResizeStarted; Chromium1.NotifyMoveOrResizeStarted;
@ -151,6 +325,20 @@ begin
end; end;
end; end;
procedure TBrowserFrame.ShowBrowser;
begin
Chromium1.WasHidden(False);
Chromium1.SendFocusEvent(True);
Chromium1.AudioMuted := False;
end;
procedure TBrowserFrame.HideBrowser;
begin
Chromium1.SendFocusEvent(False);
Chromium1.WasHidden(True);
Chromium1.AudioMuted := True;
end;
procedure TBrowserFrame.ForwardBtnClick(Sender: TObject); procedure TBrowserFrame.ForwardBtnClick(Sender: TObject);
begin begin
Chromium1.GoForward; Chromium1.GoForward;
@ -161,8 +349,7 @@ begin
Chromium1.LoadURL(URLCbx.Text); Chromium1.LoadURL(URLCbx.Text);
end; end;
procedure TBrowserFrame.Chromium1AfterCreated(Sender: TObject; procedure TBrowserFrame.Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
const browser: ICefBrowser);
begin begin
PostMessage(Handle, CEF_AFTERCREATED, 0, 0); PostMessage(Handle, CEF_AFTERCREATED, 0, 0);
end; end;
@ -172,42 +359,67 @@ begin
Chromium1.GoBack; Chromium1.GoBack;
end; end;
procedure TBrowserFrame.Chromium1AddressChange(Sender: TObject; procedure TBrowserFrame.Chromium1AddressChange( Sender : TObject;
const browser: ICefBrowser; const frame: ICefFrame; const url: ustring); const browser : ICefBrowser;
const frame : ICefFrame;
const url : ustring);
begin begin
if (URLCbx.Items.IndexOf(url) < 0) then URLCbx.Items.Add(url); PendingAddress := url;
PostMessage(Handle, CEF_UPDATEADDRESS, 0, 0);
URLCbx.Text := url;
end; end;
procedure TBrowserFrame.Chromium1BeforeClose(Sender: TObject; procedure TBrowserFrame.Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
const browser: ICefBrowser);
begin begin
if assigned(FOnBrowserDestroyed) then FOnBrowserDestroyed(self); if assigned(FOnBrowserDestroyed) then FOnBrowserDestroyed(self);
end; end;
procedure TBrowserFrame.Chromium1BeforePopup(Sender: TObject; procedure TBrowserFrame.Chromium1BeforePopup( Sender : TObject;
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, const browser : ICefBrowser;
targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition; const frame : ICefFrame;
userGesture: Boolean; const popupFeatures: TCefPopupFeatures; const targetUrl : ustring;
var windowInfo: TCefWindowInfo; var client: ICefClient; const targetFrameName : ustring;
var settings: TCefBrowserSettings; var extra_info: ICefDictionaryValue; targetDisposition : TCefWindowOpenDisposition;
var noJavascriptAccess, Result: Boolean); userGesture : Boolean;
const popupFeatures : TCefPopupFeatures;
var windowInfo : TCefWindowInfo;
var client : ICefClient;
var settings : TCefBrowserSettings;
var extra_info : ICefDictionaryValue;
var noJavascriptAccess : Boolean;
var Result : Boolean);
begin begin
// For simplicity, this demo blocks all popup windows and new tabs Result := not(assigned(Parent) and
Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB, WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]); (Parent is TBrowserTab) and
TBrowserTab(Parent).DoOnBeforePopup(windowInfo, client, targetFrameName, popupFeatures, targetDisposition));
end; end;
procedure TBrowserFrame.Chromium1Close(Sender: TObject; procedure TBrowserFrame.Chromium1OpenUrlFromTab( Sender : TObject;
const browser: ICefBrowser; var aAction: TCefCloseBrowserAction); const browser : ICefBrowser;
const frame : ICefFrame;
const targetUrl : ustring;
targetDisposition : TCefWindowOpenDisposition;
userGesture : Boolean;
out Result : Boolean);
begin
Result := assigned(Parent) and
(Parent is TBrowserTab) and
TBrowserTab(Parent).DoOpenUrlFromTab(targetUrl, targetDisposition);
end;
procedure TBrowserFrame.Chromium1Close( Sender : TObject;
const browser : ICefBrowser;
var aAction : TCefCloseBrowserAction);
begin begin
PostMessage(Handle, CEF_DESTROY, 0, 0); PostMessage(Handle, CEF_DESTROY, 0, 0);
aAction := cbaDelay; aAction := cbaDelay;
end; end;
procedure TBrowserFrame.Chromium1LoadError(Sender: TObject; procedure TBrowserFrame.Chromium1LoadError( Sender : TObject;
const browser: ICefBrowser; const frame: ICefFrame; errorCode: Integer; const browser : ICefBrowser;
const errorText, failedUrl: ustring); const frame : ICefFrame;
errorCode : Integer;
const errorText : ustring;
const failedUrl : ustring);
var var
TempString : string; TempString : string;
begin begin
@ -221,48 +433,38 @@ begin
Chromium1.LoadString(TempString, frame); Chromium1.LoadString(TempString, frame);
end; end;
procedure TBrowserFrame.Chromium1LoadingStateChange(Sender: TObject; procedure TBrowserFrame.Chromium1LoadingStateChange( Sender : TObject;
const browser: ICefBrowser; isLoading, canGoBack, canGoForward: Boolean); const browser : ICefBrowser;
isLoading : Boolean;
canGoBack : Boolean;
canGoForward : Boolean);
begin begin
BackBtn.Enabled := canGoBack; PendingIsLoading := isLoading;
ForwardBtn.Enabled := canGoForward; PendingCanGoBack := canGoBack;
PendingCanGoForward := canGoForward;
if isLoading then PostMessage(Handle, CEF_UPDATESTATE, 0, 0);
begin
ReloadBtn.Enabled := False;
StopBtn.Enabled := True;
end
else
begin
ReloadBtn.Enabled := True;
StopBtn.Enabled := False;
end;
end; end;
procedure TBrowserFrame.Chromium1OpenUrlFromTab(Sender: TObject; procedure TBrowserFrame.Chromium1StatusMessage( Sender : TObject;
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl: ustring; const browser : ICefBrowser;
targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; const value : ustring);
out Result: Boolean);
begin begin
// For simplicity, this demo blocks all popup windows and new tabs PendingStatus := value;
Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB, WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]);
PostMessage(Handle, CEF_UPDATESTATUSTEXT, 0, 0);
end; end;
procedure TBrowserFrame.Chromium1StatusMessage(Sender: TObject; procedure TBrowserFrame.Chromium1TitleChange( Sender : TObject;
const browser: ICefBrowser; const value: ustring); const browser : ICefBrowser;
const title : ustring);
begin begin
StatusBar1.Panels[0].Text := value;
end;
procedure TBrowserFrame.Chromium1TitleChange(Sender: TObject;
const browser: ICefBrowser; const title: ustring);
begin
if not(assigned(FOnBrowserTitleChange)) then exit;
if (length(title) > 0) then if (length(title) > 0) then
FOnBrowserTitleChange(self, title) PendingTitle := title
else else
FOnBrowserTitleChange(self, Chromium1.DocumentURL); PendingTitle := Chromium1.DocumentURL;
PostMessage(Handle, CEF_UPDATECAPTION, 0, 0);
end; end;
procedure TBrowserFrame.BrowserCreatedMsg(var aMessage : TMessage); procedure TBrowserFrame.BrowserCreatedMsg(var aMessage : TMessage);
@ -276,6 +478,65 @@ begin
CEFWindowParent1.Free; CEFWindowParent1.Free;
end; end;
procedure TBrowserFrame.BrowserUpdateCaptionMsg(var aMessage : TMessage);
begin
if assigned(FOnBrowserTitleChange) then
FOnBrowserTitleChange(self, PendingTitle);
end;
procedure TBrowserFrame.BrowserUpdateAddressMsg(var aMessage : TMessage);
var
TempAddress : string;
begin
TempAddress := PendingAddress;
if (URLCbx.Items.IndexOf(TempAddress) < 0) then
URLCbx.Items.Add(TempAddress);
URLCbx.Text := TempAddress;
end;
procedure TBrowserFrame.BrowserUpdateStateMsg(var aMessage : TMessage);
begin
BackBtn.Enabled := PendingCanGoBack;
ForwardBtn.Enabled := PendingCanGoForward;
if PendingIsLoading then
begin
ReloadBtn.Enabled := False;
StopBtn.Enabled := True;
end
else
begin
ReloadBtn.Enabled := True;
StopBtn.Enabled := False;
end;
end;
procedure TBrowserFrame.BrowserUpdateStatusTextMsg(var aMessage : TMessage);
begin
StatusBar1.Panels[0].Text := PendingStatus;
end;
function TBrowserFrame.CreateClientHandler(var windowInfo : TCefWindowInfo;
var client : ICefClient;
const targetFrameName : string;
const popupFeatures : TCefPopupFeatures) : boolean;
var
TempRect : TRect;
begin
if CEFWindowParent1.HandleAllocated and
Chromium1.CreateClientHandler(client, False) then
begin
Result := True;
TempRect := CEFWindowParent1.ClientRect;
WindowInfoAsChild(windowInfo, CEFWindowParent1.Handle, TempRect, '');
end
else
Result := False;
end;
end. end.

View File

@ -1,4 +1,4 @@
// ************************************************************************ // ************************************************************************
// ***************************** CEF4Delphi ******************************* // ***************************** CEF4Delphi *******************************
// ************************************************************************ // ************************************************************************
// //
@ -44,9 +44,14 @@ unit uBrowserTab;
interface interface
uses uses
{$IFDEF DELPHI16_UP}
Winapi.Windows, System.Classes, Winapi.Messages, Vcl.ComCtrls, Vcl.Controls,
Vcl.Forms, System.SysUtils,
{$ELSE}
LCLIntf, LCLType, LMessages, Classes, Messages, ComCtrls, Controls, LCLIntf, LCLType, LMessages, Classes, Messages, ComCtrls, Controls,
Forms, Forms, SysUtils,
uBrowserFrame; {$ENDIF}
uCEFInterfaces, uCEFTypes, uBrowserFrame;
type type
TBrowserTab = class(TTabSheet) TBrowserTab = class(TTabSheet)
@ -55,6 +60,8 @@ type
FTabID : cardinal; FTabID : cardinal;
function GetParentForm : TCustomForm; function GetParentForm : TCustomForm;
function GetInitialized : boolean;
function GetClosing : boolean;
function PostFormMessage(aMsg : cardinal; aWParam : WPARAM = 0; aLParam : LPARAM = 0) : boolean; function PostFormMessage(aMsg : cardinal; aWParam : WPARAM = 0; aLParam : LPARAM = 0) : boolean;
@ -66,10 +73,18 @@ type
public public
constructor Create(AOwner: TComponent; aTabID : cardinal; const aCaption : string); reintroduce; constructor Create(AOwner: TComponent; aTabID : cardinal; const aCaption : string); reintroduce;
procedure NotifyMoveOrResizeStarted; procedure NotifyMoveOrResizeStarted;
procedure CreateFrame(const aHomepage : string = '');
procedure CreateBrowser(const aHomepage : string); procedure CreateBrowser(const aHomepage : string);
procedure CloseBrowser; procedure CloseBrowser;
procedure ShowBrowser;
procedure HideBrowser;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean;
function DoOnBeforePopup(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures; targetDisposition : TCefWindowOpenDisposition) : boolean;
function DoOpenUrlFromTab(const targetUrl : string; targetDisposition : TCefWindowOpenDisposition) : boolean;
property TabID : cardinal read FTabID; property TabID : cardinal read FTabID;
property Closing : boolean read GetClosing;
property Initialized : boolean read GetInitialized;
end; end;
implementation implementation
@ -101,6 +116,18 @@ begin
Result := nil; Result := nil;
end; end;
function TBrowserTab.GetInitialized : boolean;
begin
Result := (FBrowserFrame <> nil) and
FBrowserFrame.Initialized;
end;
function TBrowserTab.GetClosing : boolean;
begin
Result := (FBrowserFrame <> nil) and
FBrowserFrame.Closing;
end;
function TBrowserTab.PostFormMessage(aMsg : cardinal; aWParam : WPARAM; aLParam : LPARAM) : boolean; function TBrowserTab.PostFormMessage(aMsg : cardinal; aWParam : WPARAM; aLParam : LPARAM) : boolean;
var var
TempForm : TCustomForm; TempForm : TCustomForm;
@ -116,17 +143,28 @@ begin
FBrowserFrame.NotifyMoveOrResizeStarted; FBrowserFrame.NotifyMoveOrResizeStarted;
end; end;
procedure TBrowserTab.CreateBrowser(const aHomepage : string); procedure TBrowserTab.CreateFrame(const aHomepage : string);
begin begin
if (FBrowserFrame = nil) then
begin
FBrowserFrame := TBrowserFrame.Create(self); FBrowserFrame := TBrowserFrame.Create(self);
FBrowserFrame.Name := 'BrowserFrame' + IntToStr(TabID);
FBrowserFrame.Parent := self; FBrowserFrame.Parent := self;
FBrowserFrame.Align := alClient; FBrowserFrame.Align := alClient;
FBrowserFrame.Visible := True; FBrowserFrame.Visible := True;
FBrowserFrame.Homepage := aHomepage;
FBrowserFrame.OnBrowserDestroyed := BrowserFrame_OnBrowserDestroyed; FBrowserFrame.OnBrowserDestroyed := BrowserFrame_OnBrowserDestroyed;
FBrowserFrame.OnBrowserTitleChange := BrowserFrame_OnBrowserTitleChange; FBrowserFrame.OnBrowserTitleChange := BrowserFrame_OnBrowserTitleChange;
FBrowserFrame.CreateAllHandles;
end;
FBrowserFrame.CreateBrowser; FBrowserFrame.Homepage := aHomepage;
end;
procedure TBrowserTab.CreateBrowser(const aHomepage : string);
begin
CreateFrame(aHomepage);
if (FBrowserFrame <> nil) then FBrowserFrame.CreateBrowser;
end; end;
procedure TBrowserTab.CloseBrowser; procedure TBrowserTab.CloseBrowser;
@ -134,6 +172,16 @@ begin
if (FBrowserFrame <> nil) then FBrowserFrame.CloseBrowser; if (FBrowserFrame <> nil) then FBrowserFrame.CloseBrowser;
end; end;
procedure TBrowserTab.ShowBrowser;
begin
if (FBrowserFrame <> nil) then FBrowserFrame.ShowBrowser;
end;
procedure TBrowserTab.HideBrowser;
begin
if (FBrowserFrame <> nil) then FBrowserFrame.HideBrowser;
end;
procedure TBrowserTab.BrowserFrame_OnBrowserDestroyed(Sender: TObject); procedure TBrowserTab.BrowserFrame_OnBrowserDestroyed(Sender: TObject);
begin begin
// This event is executed in a CEF thread so we have to send a message to // This event is executed in a CEF thread so we have to send a message to
@ -146,4 +194,38 @@ begin
Caption := aTitle; Caption := aTitle;
end; end;
function TBrowserTab.CreateClientHandler(var windowInfo : TCefWindowInfo;
var client : ICefClient;
const targetFrameName : string;
const popupFeatures : TCefPopupFeatures) : boolean;
begin
Result := (FBrowserFrame <> nil) and
FBrowserFrame.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures);
end;
function TBrowserTab.DoOnBeforePopup(var windowInfo : TCefWindowInfo;
var client : ICefClient;
const targetFrameName : string;
const popupFeatures : TCefPopupFeatures;
targetDisposition : TCefWindowOpenDisposition) : boolean;
var
TempForm : TCustomForm;
begin
TempForm := ParentForm;
Result := (TempForm <> nil) and
(TempForm is TMainForm) and
TMainForm(TempForm).DoOnBeforePopup(windowInfo, client, targetFrameName, popupFeatures, targetDisposition);
end;
function TBrowserTab.DoOpenUrlFromTab(const targetUrl : string;
targetDisposition : TCefWindowOpenDisposition) : boolean;
var
TempForm : TCustomForm;
begin
TempForm := ParentForm;
Result := (TempForm <> nil) and
(TempForm is TMainForm) and
TMainForm(TempForm).DoOpenUrlFromTab(targetUrl, targetDisposition);
end;
end. end.

View File

@ -0,0 +1,37 @@
object ChildForm: TChildForm
Left = 0
Top = 0
Caption = 'Popup'
ClientHeight = 256
ClientWidth = 352
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
Position = poScreenCenter
OnClose = FormClose
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
object CEFWindowParent1: TCEFWindowParent
Left = 0
Top = 0
Width = 352
Height = 256
Align = alClient
TabOrder = 0
end
object Chromium1: TChromium
OnTitleChange = Chromium1TitleChange
OnBeforePopup = Chromium1BeforePopup
OnAfterCreated = Chromium1AfterCreated
OnBeforeClose = Chromium1BeforeClose
OnClose = Chromium1Close
OnOpenUrlFromTab = Chromium1OpenUrlFromTab
Left = 24
Top = 56
end
end

View File

@ -0,0 +1,314 @@
// ************************************************************************
// ***************************** CEF4Delphi *******************************
// ************************************************************************
//
// CEF4Delphi is based on DCEF3 which uses CEF to embed a chromium-based
// browser in Delphi applications.
//
// The original license of DCEF3 still applies to CEF4Delphi.
//
// For more information about CEF4Delphi visit :
// https://www.briskbard.com/index.php?lang=en&pageid=cef
//
// Copyright © 2021 Salvador Diaz Fau. All rights reserved.
//
// ************************************************************************
// ************ vvvv Original license and comments below vvvv *************
// ************************************************************************
(*
* Delphi Chromium Embedded 3
*
* Usage allowed under the restrictions of the Lesser GNU General Public License
* or alternatively the restrictions of the Mozilla Public License 1.1
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* Unit owner : Henri Gourvest <hgourvest@gmail.com>
* Web site : http://www.progdigy.com
* Repository : http://code.google.com/p/delphichromiumembedded/
* Group : http://groups.google.com/group/delphichromiumembedded
*
* Embarcadero Technologies, Inc is not permitted to use or redistribute
* this source code without explicit permission.
*
*)
unit uChildForm;
{$MODE Delphi}
{$I cef.inc}
interface
uses
{$IFDEF DELPHI16_UP}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
System.SyncObjs, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
Vcl.ExtCtrls,
{$ELSE}
LCLIntf, LCLType, LMessages, Messages, SysUtils, Variants, Classes, SyncObjs,
Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,
{$ENDIF}
uCEFChromium, uCEFTypes, uCEFInterfaces, uCEFConstants, uCEFWindowParent, uCEFWinControl,
uCEFChromiumCore;
const
CEF_UPDATECAPTION = WM_APP + $A55;
type
TChildForm = class(TForm)
Chromium1: TChromium;
CEFWindowParent1: TCEFWindowParent;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
procedure Chromium1BeforePopup(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; const popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo; var client: ICefClient; var settings: TCefBrowserSettings; var extra_info: ICefDictionaryValue; var noJavascriptAccess: Boolean; var Result: Boolean);
procedure Chromium1TitleChange(Sender: TObject; const browser: ICefBrowser; const title: ustring);
procedure Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction);
procedure Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
procedure Chromium1OpenUrlFromTab(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; out Result: Boolean);
protected
FCriticalSection : TCriticalSection;
FCanClose : boolean;
FClosing : boolean;
FBrowserWasCreated : boolean;
FTitle : string;
FPopupFeatures : TCefPopupFeatures;
function GetInitialized : boolean;
procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
procedure BrowserDestroyMsg(var aMessage : TMessage); message CEF_DESTROY;
procedure BrowserUpdateCaptionMsg(var aMessage : TMessage); message CEF_UPDATECAPTION;
public
procedure AfterConstruction; override;
function CreateBrowser(const aHomepage : string) : boolean;
function CreateClientHandler(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures) : boolean;
procedure ApplyPopupFeatures;
property Initialized : boolean read GetInitialized;
property Closing : boolean read FClosing;
end;
implementation
{$R *.lfm}
uses
{$IFDEF DELPHI16_UP}
System.Math,
{$ELSE}
Math,
{$ENDIF}
uCEFMiscFunctions, uCEFApplication, uMainForm;
// Destruction steps
// =================
// 1. FormCloseQuery sets CanClose to FALSE calls TChromium.CloseBrowser which
// triggers the TChromium.OnClose event.
// 2. TChromium.OnClose sends a CEFBROWSER_DESTROY message to destroy CEFWindowParent1
// in the main thread, which triggers the TChromium.OnBeforeClose event.
// 3. TChromium.OnBeforeClose sets FCanClose := True and sends WM_CLOSE to the form.
procedure TChildForm.AfterConstruction;
begin
inherited AfterConstruction;
CreateHandle;
CEFWindowParent1.CreateHandle;
end;
function TChildForm.CreateClientHandler(var windowInfo : TCefWindowInfo;
var client : ICefClient;
const targetFrameName : string;
const popupFeatures : TCefPopupFeatures) : boolean;
var
TempRect : TRect;
begin
if CEFWindowParent1.HandleAllocated and
Chromium1.CreateClientHandler(client, False) then
begin
Result := True;
FPopupFeatures := popupFeatures;
TempRect := CEFWindowParent1.ClientRect;
if (FPopupFeatures.widthset <> 0) then TempRect.Right := max(FPopupFeatures.width, 100);
if (FPopupFeatures.heightset <> 0) then TempRect.Bottom := max(FPopupFeatures.height, 100);
WindowInfoAsChild(windowInfo, CEFWindowParent1.Handle, TempRect, '');
end
else
Result := False;
end;
function TChildForm.CreateBrowser(const aHomepage : string) : boolean;
begin
Chromium1.DefaultURL := aHomepage;
Result := Chromium1.CreateBrowser(CEFWindowParent1);
end;
procedure TChildForm.ApplyPopupFeatures;
begin
if (FPopupFeatures.xset <> 0) then Chromium1.SetFormLeftTo(FPopupFeatures.x);
if (FPopupFeatures.yset <> 0) then Chromium1.SetFormTopTo(FPopupFeatures.y);
if (FPopupFeatures.widthset <> 0) then Chromium1.ResizeFormWidthTo(FPopupFeatures.width);
if (FPopupFeatures.heightset <> 0) then Chromium1.ResizeFormHeightTo(FPopupFeatures.height);
end;
procedure TChildForm.Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser);
begin
FBrowserWasCreated := True;
end;
procedure TChildForm.Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
begin
FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0);
end;
procedure TChildForm.Chromium1BeforePopup( Sender : TObject;
const browser : ICefBrowser;
const frame : ICefFrame;
const targetUrl : ustring;
const targetFrameName : ustring;
targetDisposition : TCefWindowOpenDisposition;
userGesture : Boolean;
const popupFeatures : TCefPopupFeatures;
var windowInfo : TCefWindowInfo;
var client : ICefClient;
var settings : TCefBrowserSettings;
var extra_info : ICefDictionaryValue;
var noJavascriptAccess : Boolean;
var Result : Boolean);
begin
Result := not(TMainForm(Owner).DoOnBeforePopup(windowInfo, client, targetFrameName, popupFeatures, targetDisposition));
end;
procedure TChildForm.Chromium1OpenUrlFromTab( Sender : TObject;
const browser : ICefBrowser;
const frame : ICefFrame;
const targetUrl : ustring;
targetDisposition : TCefWindowOpenDisposition;
userGesture : Boolean;
out Result : Boolean);
begin
Result := not(TMainForm(Owner).DoOpenUrlFromTab(targetUrl, targetDisposition));
end;
procedure TChildForm.Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction);
begin
PostMessage(Handle, CEF_DESTROY, 0, 0);
aAction := cbaDelay;
end;
procedure TChildForm.Chromium1TitleChange(Sender: TObject; const browser: ICefBrowser; const title: ustring);
begin
try
FCriticalSection.Acquire;
FTitle := title;
finally
FCriticalSection.Release;
PostMessage(Handle, CEF_UPDATECAPTION, 0, 0);
end;
end;
function TChildForm.GetInitialized : boolean;
begin
Result := Chromium1.Initialized;
end;
procedure TChildForm.WMMove(var aMessage : TWMMove);
begin
inherited;
if (Chromium1 <> nil) then Chromium1.NotifyMoveOrResizeStarted;
end;
procedure TChildForm.WMMoving(var aMessage : TMessage);
begin
inherited;
if (Chromium1 <> nil) then Chromium1.NotifyMoveOrResizeStarted;
end;
procedure TChildForm.WMEnterMenuLoop(var aMessage: TMessage);
begin
inherited;
if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then GlobalCEFApp.OsmodalLoop := True;
end;
procedure TChildForm.WMExitMenuLoop(var aMessage: TMessage);
begin
inherited;
if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then GlobalCEFApp.OsmodalLoop := False;
end;
procedure TChildForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
procedure TChildForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if FBrowserWasCreated then
begin
CanClose := FCanClose;
if not(FClosing) then
begin
FClosing := True;
Visible := False;
Chromium1.CloseBrowser(True);
end;
end
else
CanClose := True;
end;
procedure TChildForm.FormCreate(Sender: TObject);
begin
FCriticalSection := TCriticalSection.Create;
FBrowserWasCreated := False;
FCanClose := False;
FClosing := False;
end;
procedure TChildForm.FormDestroy(Sender: TObject);
begin
FCriticalSection.Free;
if FBrowserWasCreated and TMainForm(Owner).HandleAllocated then
PostMessage(TMainForm(Owner).Handle, CEF_CHILDDESTROYED, 0, 0);
end;
procedure TChildForm.BrowserDestroyMsg(var aMessage : TMessage);
begin
CEFWindowParent1.Free;
end;
procedure TChildForm.BrowserUpdateCaptionMsg(var aMessage : TMessage);
begin
try
FCriticalSection.Acquire;
Caption := FTitle;
finally
FCriticalSection.Release;
end;
end;
end.

View File

@ -1,62 +1,66 @@
object MainForm: TMainForm object MainForm: TMainForm
Left = 357 Left = 0
Height = 704 Top = 0
Top = 89
Width = 991
Caption = 'Initializing. Please, wait...' Caption = 'Initializing. Please, wait...'
ClientHeight = 704 ClientHeight = 703
ClientWidth = 991 ClientWidth = 991
Color = clBtnFace Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -11 Font.Height = -11
Font.Name = 'Tahoma' Font.Name = 'Tahoma'
Font.Style = []
Position = poScreenCenter
OnCloseQuery = FormCloseQuery OnCloseQuery = FormCloseQuery
OnCreate = FormCreate OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow OnShow = FormShow
Position = poScreenCenter PixelsPerInch = 96
LCLVersion = '2.0.6.0'
object BrowserPageCtrl: TPageControl object BrowserPageCtrl: TPageControl
Left = 34 Left = 32
Height = 704
Top = 0 Top = 0
Width = 957 Width = 959
Height = 703
Align = alClient Align = alClient
TabOrder = 0 TabOrder = 0
TabWidth = 150
end end
object ButtonPnl: TPanel object ButtonPnl: TPanel
Left = 0 Left = 0
Height = 704
Top = 0 Top = 0
Width = 34 Width = 32
Height = 703
Align = alLeft Align = alLeft
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 704
ClientWidth = 34
Enabled = False Enabled = False
TabOrder = 1 TabOrder = 1
object AddTabBtn: TSpeedButton object AddTabBtn: TSpeedButton
Left = 4 Left = 1
Height = 26 Top = 1
Top = 4
Width = 26 Width = 26
Height = 26
Caption = '+' Caption = '+'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -24 Font.Height = -24
Font.Name = 'Arial Black' Font.Name = 'Arial Black'
OnClick = AddTabBtnClick Font.Style = []
ParentFont = False ParentFont = False
OnClick = AddTabBtnClick
end end
object RemoveTabBtn: TSpeedButton object RemoveTabBtn: TSpeedButton
Left = 4 Left = 1
Height = 26 Top = 30
Top = 33
Width = 26 Width = 26
Height = 26
Caption = '−' Caption = '−'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -24 Font.Height = -24
Font.Name = 'Arial Black' Font.Name = 'Arial Black'
OnClick = RemoveTabBtnClick Font.Style = []
ParentFont = False ParentFont = False
OnClick = RemoveTabBtnClick
end end
end end
end end

View File

@ -44,13 +44,21 @@ unit uMainForm;
interface interface
uses uses
LCLIntf, LCLType, LMessages, Messages, SysUtils, Variants, Classes, Graphics, {$IFDEF DELPHI16_UP}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, System.SyncObjs,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.ToolWin, Vcl.Buttons, Vcl.ExtCtrls,
{$ELSE}
LCLIntf, LCLType, LMessages, Messages, SysUtils, Variants, Classes, Graphics, SyncObjs,
Controls, Forms, Dialogs, ComCtrls, ToolWin, Buttons, ExtCtrls, Controls, Forms, Dialogs, ComCtrls, ToolWin, Buttons, ExtCtrls,
uCEFApplication, uCEFTypes, uCEFConstants; {$ENDIF}
uCEFApplication, uCEFInterfaces, uCEFTypes, uCEFConstants, uChildForm, uBrowserTab;
const const
CEF_INITIALIZED = WM_APP + $100; CEF_INITIALIZED = WM_APP + $A50;
CEF_DESTROYTAB = WM_APP + $101; CEF_DESTROYTAB = WM_APP + $A51;
CEF_CREATENEXTCHILD = WM_APP + $A52;
CEF_CREATENEXTTAB = WM_APP + $A53;
CEF_CHILDDESTROYED = WM_APP + $A54;
HOMEPAGE_URL = 'https://www.google.com'; HOMEPAGE_URL = 'https://www.google.com';
DEFAULT_TAB_CAPTION = 'New tab'; DEFAULT_TAB_CAPTION = 'New tab';
@ -68,28 +76,44 @@ type
procedure FormShow(Sender: TObject); procedure FormShow(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormDestroy(Sender: TObject);
protected protected
// Variables to control when can we destroy the form safely FHiddenTab : TBrowserTab;
FChildForm : TChildForm;
FCriticalSection : TCriticalSection;
FCanClose : boolean; FCanClose : boolean;
FClosing : boolean; FClosing : boolean; // Set to True in the CloseQuery event.
FLastTabID : cardinal; // Used by NextTabID to generate unique tab IDs FLastTabID : cardinal; // Used by NextTabID to generate unique tab IDs
FPendingURL : string;
function GetNextTabID : cardinal; function GetNextTabID : cardinal;
function GetPopupChildCount : integer;
function GetBrowserTabCount : integer;
procedure EnableButtonPnl; procedure EnableButtonPnl;
function CloseAllTabs : boolean; function CloseAllBrowsers : boolean;
procedure CloseTab(aIndex : integer); procedure CloseTab(aIndex : integer);
procedure CreateHiddenBrowsers;
procedure CEFInitializedMsg(var aMessage : TMessage); message CEF_INITIALIZED; procedure CEFInitializedMsg(var aMessage : TMessage); message CEF_INITIALIZED;
procedure DestroyTabMsg(var aMessage : TMessage); message CEF_DESTROYTAB; procedure DestroyTabMsg(var aMessage : TMessage); message CEF_DESTROYTAB;
procedure CreateNextChildMsg(var aMessage : TMessage); message CEF_CREATENEXTCHILD;
procedure CreateNextTabMsg(var aMessage : TMessage); message CEF_CREATENEXTTAB;
procedure ChildDestroyedMsg(var aMessage : TMessage); message CEF_CHILDDESTROYED;
procedure WMMove(var aMessage : TWMMove); message WM_MOVE; procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
procedure WMMoving(var aMessage : TMessage); message WM_MOVING; procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP; procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP; procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
procedure WMQueryEndSession(var aMessage: TWMQueryEndSession); message WM_QUERYENDSESSION;
property NextTabID : cardinal read GetNextTabID; property NextTabID : cardinal read GetNextTabID;
property PopupChildCount : integer read GetPopupChildCount;
property BrowserTabCount : integer read GetBrowserTabCount;
public
function DoOnBeforePopup(var windowInfo : TCefWindowInfo; var client : ICefClient; const targetFrameName : string; const popupFeatures : TCefPopupFeatures; targetDisposition : TCefWindowOpenDisposition) : boolean;
function DoOpenUrlFromTab(const targetUrl : string; targetDisposition : TCefWindowOpenDisposition) : boolean;
end; end;
var var
@ -101,8 +125,6 @@ implementation
{$R *.lfm} {$R *.lfm}
uses
uBrowserTab;
// This demo shows how to use a TPageControl with TFrames that include // This demo shows how to use a TPageControl with TFrames that include
// CEF4Delphi browsers. // CEF4Delphi browsers.
@ -129,14 +151,38 @@ uses
// event which will be used in TBrowserTab to send a CEF_DESTROYTAB message // event which will be used in TBrowserTab to send a CEF_DESTROYTAB message
// to the main form to free the tab. // to the main form to free the tab.
// This demo also uses custom forms to open popup browsers in the same way as
// the PopupBrowser2 demo. Please, read the code comments in that demo for all
// details about handling the custom child forms.
// Additionally, this demo also creates new tabs when a browser triggers the
// TChromium.OnBeforePopup event.
// VCL components *MUST* be created and destroyed in the main thread but CEF
// executes the TChromium.OnBeforePopup in a different thread.
// For this reason this demo creates a hidden popup form (TChildForm) and a
// hidden TBrowserTab in case CEF needs to show a popup window.
// TChromium.OnBeforePopup calls TMainForm.DoOnBeforePopup to handle all the
// events in the same place.
// TMainForm.DoOnBeforePopup will call CreateClientHandler to initialize some
// parameters and create the new ICefClient using the hidden form or tab.
// After that, it sends a custom message to show the popup form or tab and create
// a new one.
// To close safely this demo you must close all the browser tabs first following // To close safely this demo you must close all the browser tabs first following
// this steps : // this steps :
// //
// 1. FormCloseQuery sets CanClose to FALSE and calls CloseAllTabs and FClosing // 1. FormCloseQuery sets CanClose to FALSE and calls CloseAllBrowsers and FClosing
// is set to TRUE. // is set to TRUE.
// 2. Each tab will send a CEF_DESTROYTAB message to free that tab. // 2. Each tab will send a CEF_DESTROYTAB message to the main form to free that tab.
// 3. When TPageControl has no tabs then we can set FCanClose to TRUE and send a // 3. Each child form will send a CEF_CHILDDESTROYED message to the main form.
// WM_CLOSE to the main form to close the application. // 3. When TPageControl has no tabs and all the child forms are also closed then we
// can set FCanClose to TRUE and send a WM_CLOSE message to the main form to
// close the application.
procedure GlobalCEFApp_OnContextInitialized; procedure GlobalCEFApp_OnContextInitialized;
begin begin
@ -148,7 +194,12 @@ procedure CreateGlobalCEFApp;
begin begin
GlobalCEFApp := TCefApplication.Create; GlobalCEFApp := TCefApplication.Create;
GlobalCEFApp.cache := 'cache'; GlobalCEFApp.cache := 'cache';
GlobalCEFApp.EnablePrintPreview := True;
GlobalCEFApp.OnContextInitialized := GlobalCEFApp_OnContextInitialized; GlobalCEFApp.OnContextInitialized := GlobalCEFApp_OnContextInitialized;
// This is a workaround for the CEF4Delphi issue #324 :
// https://github.com/salvadordf/CEF4Delphi/issues/324
GlobalCEFApp.DisableFeatures := 'WinUseBrowserSpellChecker';
end; end;
procedure TMainForm.EnableButtonPnl; procedure TMainForm.EnableButtonPnl;
@ -158,7 +209,7 @@ begin
ButtonPnl.Enabled := True; ButtonPnl.Enabled := True;
Caption := 'Tabbed Browser 2'; Caption := 'Tabbed Browser 2';
cursor := crDefault; cursor := crDefault;
if (BrowserPageCtrl.PageCount = 0) then AddTabBtn.Click; if (BrowserTabCount = 0) then AddTabBtn.Click;
end; end;
end; end;
@ -168,6 +219,44 @@ begin
Result := FLastTabID; Result := FLastTabID;
end; end;
function TMainForm.GetPopupChildCount : integer;
var
i : integer;
TempForm : TCustomForm;
begin
Result := 0;
i := pred(screen.CustomFormCount);
while (i >= 0) do
begin
// Only count the fully initialized child forms and not the one waiting to be used.
TempForm := screen.CustomForms[i];
if (TempForm is TChildForm) and
TChildForm(TempForm).Initialized then
inc(Result);
dec(i);
end;
end;
function TMainForm.GetBrowserTabCount : integer;
var
i : integer;
begin
Result := 0;
i := pred(BrowserPageCtrl.PageCount);
while (i >= 0) do
begin
// Only count the fully initialized browser tabs and not the one waiting to be used.
if TBrowserTab(BrowserPageCtrl.Pages[i]).Initialized then
inc(Result);
dec(i);
end;
end;
procedure TMainForm.AddTabBtnClick(Sender: TObject); procedure TMainForm.AddTabBtnClick(Sender: TObject);
var var
TempNewTab : TBrowserTab; TempNewTab : TBrowserTab;
@ -183,6 +272,7 @@ end;
procedure TMainForm.CEFInitializedMsg(var aMessage : TMessage); procedure TMainForm.CEFInitializedMsg(var aMessage : TMessage);
begin begin
EnableButtonPnl; EnableButtonPnl;
CreateHiddenBrowsers;
end; end;
procedure TMainForm.DestroyTabMsg(var aMessage : TMessage); procedure TMainForm.DestroyTabMsg(var aMessage : TMessage);
@ -190,6 +280,8 @@ var
i : integer; i : integer;
TempTab : TBrowserTab; TempTab : TBrowserTab;
begin begin
// Every tab sends a CEF_DESTROYTAB message when its browser has been destroyed
// and then we can destroy the TBrowserTab control.
i := 0; i := 0;
while (i < BrowserPageCtrl.PageCount) do while (i < BrowserPageCtrl.PageCount) do
begin begin
@ -204,13 +296,74 @@ begin
inc(i); inc(i);
end; end;
if FClosing and (BrowserPageCtrl.PageCount = 0) then // Here we check if this was the last initialized browser to close the
// application safely.
if FClosing and (PopupChildCount = 0) and (BrowserTabCount = 0) then
begin begin
FCanClose := True; FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0); PostMessage(Handle, WM_CLOSE, 0, 0);
end; end;
end; end;
procedure TMainForm.ChildDestroyedMsg(var aMessage : TMessage);
begin
// Every destroyed child form sends a CEF_CHILDDESTROYED message
// Here we check if this was the last initialized browser to close the
// application safely.
if FClosing and (PopupChildCount = 0) and (BrowserTabCount = 0) then
begin
FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0);
end;
end;
procedure TMainForm.CreateNextChildMsg(var aMessage : TMessage);
begin
try
FCriticalSection.Acquire;
if (FChildForm <> nil) then
begin
if (aMessage.lParam <> 0) then
FChildForm.CreateBrowser(FPendingURL)
else
FChildForm.ApplyPopupFeatures;
FChildForm.Show;
end;
FChildForm := TChildForm.Create(self);
finally
FCriticalSection.Release;
end;
end;
procedure TMainForm.CreateNextTabMsg(var aMessage : TMessage);
begin
try
FCriticalSection.Acquire;
if (FHiddenTab <> nil) then
begin
FHiddenTab.TabVisible := True;
FHiddenTab.PageIndex := pred(BrowserPageCtrl.PageCount);
if (aMessage.lParam <> 0) then
FHiddenTab.CreateBrowser(FPendingURL);
BrowserPageCtrl.ActivePageIndex := FHiddenTab.PageIndex;
end;
FHiddenTab := TBrowserTab.Create(self, NextTabID, DEFAULT_TAB_CAPTION);
FHiddenTab.PageControl := BrowserPageCtrl;
FHiddenTab.TabVisible := False;
FHiddenTab.CreateFrame;
finally
FCriticalSection.Release;
end;
end;
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin begin
CanClose := FCanClose; CanClose := FCanClose;
@ -220,7 +373,7 @@ begin
FClosing := True; FClosing := True;
ButtonPnl.Enabled := False; ButtonPnl.Enabled := False;
if not(CloseAllTabs) then if not(CloseAllBrowsers) then
begin begin
FCanClose := True; FCanClose := True;
PostMessage(Handle, WM_CLOSE, 0, 0); PostMessage(Handle, WM_CLOSE, 0, 0);
@ -233,30 +386,65 @@ begin
FCanClose := False; FCanClose := False;
FClosing := False; FClosing := False;
FLastTabID := 0; FLastTabID := 0;
FChildForm := nil;
FHiddenTab := nil;
FCriticalSection := TCriticalSection.Create;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
FreeAndNil(FCriticalSection);
end; end;
procedure TMainForm.FormShow(Sender: TObject); procedure TMainForm.FormShow(Sender: TObject);
begin begin
if (GlobalCEFApp <> nil) and GlobalCEFApp.GlobalContextInitialized then if (GlobalCEFApp <> nil) and GlobalCEFApp.GlobalContextInitialized then
begin
EnableButtonPnl; EnableButtonPnl;
CreateHiddenBrowsers;
end;
end; end;
procedure TMainForm.RemoveTabBtnClick(Sender: TObject); procedure TMainForm.RemoveTabBtnClick(Sender: TObject);
begin begin
// Call TBrowserTab.CloseBrowser in the active tab
CloseTab(BrowserPageCtrl.ActivePageIndex); CloseTab(BrowserPageCtrl.ActivePageIndex);
end; end;
function TMainForm.CloseAllTabs : boolean; function TMainForm.CloseAllBrowsers : boolean;
var var
i : integer; i : integer;
TempForm : TCustomForm;
TempTab : TBrowserTab;
begin begin
Result := False; Result := False;
i := pred(BrowserPageCtrl.PageCount); i := pred(screen.CustomFormCount);
while (i >= 0) do while (i >= 0) do
begin begin
TBrowserTab(BrowserPageCtrl.Pages[i]).CloseBrowser; TempForm := screen.CustomForms[i];
if (TempForm is TChildForm) and
TChildForm(TempForm).Initialized and
not(TChildForm(TempForm).Closing) then
begin
PostMessage(TempForm.Handle, WM_CLOSE, 0, 0);
Result := True; Result := True;
end;
dec(i);
end;
i := pred(BrowserPageCtrl.PageCount);
while (i >= 0) do
begin
TempTab := TBrowserTab(BrowserPageCtrl.Pages[i]);
if TempTab.Initialized and not(TempTab.Closing) then
begin
TempTab.CloseBrowser;
Result := True;
end;
dec(i); dec(i);
end; end;
end; end;
@ -267,6 +455,26 @@ begin
TBrowserTab(BrowserPageCtrl.Pages[aIndex]).CloseBrowser; TBrowserTab(BrowserPageCtrl.Pages[aIndex]).CloseBrowser;
end; end;
procedure TMainForm.CreateHiddenBrowsers;
begin
try
FCriticalSection.Acquire;
if (FChildForm = nil) then
FChildForm := TChildForm.Create(self);
if (FHiddenTab = nil) then
begin
FHiddenTab := TBrowserTab.Create(self, NextTabID, DEFAULT_TAB_CAPTION);
FHiddenTab.PageControl := BrowserPageCtrl;
FHiddenTab.TabVisible := False;
FHiddenTab.CreateFrame;
end;
finally
FCriticalSection.Release;
end;
end;
procedure TMainForm.WMMove(var aMessage : TWMMove); procedure TMainForm.WMMove(var aMessage : TWMMove);
var var
i : integer; i : integer;
@ -311,4 +519,73 @@ begin
GlobalCEFApp.OsmodalLoop := False; GlobalCEFApp.OsmodalLoop := False;
end; end;
procedure TMainForm.WMQueryEndSession(var aMessage: TWMQueryEndSession);
begin
// We return False (0) to close the browser correctly while we can.
// This is not what Microsoft recommends doing when an application receives
// WM_QUERYENDSESSION but at least we avoid TApplication calling HALT when
// it receives WM_ENDSESSION.
// The CEF subprocesses may receive WM_QUERYENDSESSION and WM_ENDSESSION
// before the main process and they may crash before closing the main form.
aMessage.Result := 0;
PostMessage(Handle, WM_CLOSE, 0, 0);
end;
function TMainForm.DoOnBeforePopup(var windowInfo : TCefWindowInfo;
var client : ICefClient;
const targetFrameName : string;
const popupFeatures : TCefPopupFeatures;
targetDisposition : TCefWindowOpenDisposition) : boolean;
begin
try
FCriticalSection.Acquire;
case targetDisposition of
WOD_NEW_FOREGROUND_TAB,
WOD_NEW_BACKGROUND_TAB :
Result := (FHiddenTab <> nil) and
FHiddenTab.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures) and
PostMessage(Handle, CEF_CREATENEXTTAB, 0, ord(False));
WOD_NEW_WINDOW,
WOD_NEW_POPUP :
Result := (FChildForm <> nil) and
FChildForm.CreateClientHandler(windowInfo, client, targetFrameName, popupFeatures) and
PostMessage(Handle, CEF_CREATENEXTCHILD, 0, ord(False));
else Result := False;
end;
finally
FCriticalSection.Release;
end;
end;
function TMainForm.DoOpenUrlFromTab(const targetUrl : string;
targetDisposition : TCefWindowOpenDisposition) : boolean;
begin
try
FCriticalSection.Acquire;
case targetDisposition of
WOD_NEW_FOREGROUND_TAB,
WOD_NEW_BACKGROUND_TAB :
begin
FPendingURL := targetUrl;
Result := PostMessage(Handle, CEF_CREATENEXTTAB, 0, ord(True));
end;
WOD_NEW_WINDOW,
WOD_NEW_POPUP :
begin
FPendingURL := targetUrl;
Result := PostMessage(Handle, CEF_CREATENEXTCHILD, 0, ord(True));
end
else Result := False;
end;
finally
FCriticalSection.Release;
end;
end;
end. end.

View File

@ -21,7 +21,7 @@
</CompilerOptions> </CompilerOptions>
<Description Value="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."/> <Description Value="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."/>
<License Value="MPL 1.1"/> <License Value="MPL 1.1"/>
<Version Major="91" Minor="1" Release="20"/> <Version Major="91" Minor="1" Release="21"/>
<Files Count="202"> <Files Count="202">
<Item1> <Item1>
<Filename Value="..\source\uCEFAccessibilityHandler.pas"/> <Filename Value="..\source\uCEFAccessibilityHandler.pas"/>

View File

@ -66,13 +66,13 @@ uses
const const
CEF_SUPPORTED_VERSION_MAJOR = 91; CEF_SUPPORTED_VERSION_MAJOR = 91;
CEF_SUPPORTED_VERSION_MINOR = 1; CEF_SUPPORTED_VERSION_MINOR = 1;
CEF_SUPPORTED_VERSION_RELEASE = 20; CEF_SUPPORTED_VERSION_RELEASE = 21;
CEF_SUPPORTED_VERSION_BUILD = 0; CEF_SUPPORTED_VERSION_BUILD = 0;
CEF_CHROMEELF_VERSION_MAJOR = 91; CEF_CHROMEELF_VERSION_MAJOR = 91;
CEF_CHROMEELF_VERSION_MINOR = 0; CEF_CHROMEELF_VERSION_MINOR = 0;
CEF_CHROMEELF_VERSION_RELEASE = 4472; CEF_CHROMEELF_VERSION_RELEASE = 4472;
CEF_CHROMEELF_VERSION_BUILD = 101; CEF_CHROMEELF_VERSION_BUILD = 114;
{$IFDEF MSWINDOWS} {$IFDEF MSWINDOWS}
LIBCEF_DLL = 'libcef.dll'; LIBCEF_DLL = 'libcef.dll';

View File

@ -2,9 +2,9 @@
"UpdateLazPackages" : [ "UpdateLazPackages" : [
{ {
"ForceNotify" : true, "ForceNotify" : true,
"InternalVersion" : 305, "InternalVersion" : 306,
"Name" : "cef4delphi_lazarus.lpk", "Name" : "cef4delphi_lazarus.lpk",
"Version" : "91.1.20.0" "Version" : "91.1.21.0"
} }
], ],
"UpdatePackageData" : { "UpdatePackageData" : {