2022-03-30 16:48:20 +02:00
unit uMobileBrowser;
2023-11-26 19:28:28 +01:00
{$I ..\..\..\source\cef.inc}
2022-03-30 16:48:20 +02:00
interface
uses
Winapi . Windows, Winapi . Messages, System. SysUtils, System. Variants, System. Classes, Vcl. Graphics,
Vcl. Controls, Vcl. Forms, Vcl. Dialogs, Vcl. StdCtrls, Vcl. ExtCtrls, Vcl. Mask, Vcl. Samples. Spin,
uCEFChromium, uCEFWindowParent, uCEFInterfaces, uCEFConstants, uCEFTypes, uCEFJson,
2023-11-26 19:28:28 +01:00
uCEFWinControl, uCEFChromiumCore, uCEFDictionaryValue;
2022-03-30 16:48:20 +02:00
type
TForm1 = class( TForm)
Timer1: TTimer;
Chromium1: TChromium;
Panel1: TPanel;
Panel2: TPanel;
AddressPnl: TPanel;
GoBtn: TButton;
CEFWindowParent1: TCEFWindowParent;
Splitter1: TSplitter;
LogMem: TMemo;
Panel3: TPanel;
CanEmulateBtn: TButton;
ClearDeviceMetricsOverrideBtn: TButton;
Panel4: TPanel;
GroupBox1: TGroupBox;
UserAgentCb: TComboBox;
OverrideUserAgentBtn: TButton;
EmulateTouchChk: TCheckBox;
Panel5: TPanel;
GroupBox2: TGroupBox;
Panel6: TPanel;
Label1: TLabel;
HeightEdt: TSpinEdit;
Panel7: TPanel;
Label2: TLabel;
WidthEdt: TSpinEdit;
OverrideDeviceMetricsBtn: TButton;
Panel8: TPanel;
Label3: TLabel;
ScaleEdt: TMaskEdit;
Panel9: TPanel;
Label4: TLabel;
OrientationCb: TComboBox;
Panel10: TPanel;
Label5: TLabel;
AngleEdt: TSpinEdit;
2024-06-19 11:05:42 +02:00
AddressCb: TComboBox;
2022-03-30 16:48:20 +02:00
procedure GoBtnClick( Sender: TObject) ;
procedure Timer1Timer( Sender: TObject) ;
procedure CanEmulateBtnClick( Sender: TObject) ;
procedure OverrideUserAgentBtnClick( Sender: TObject) ;
2024-04-18 12:06:34 +02:00
procedure EmulateTouchChkClick( Sender: TObject) ;
procedure ClearDeviceMetricsOverrideBtnClick( Sender: TObject) ;
procedure OverrideDeviceMetricsBtnClick( Sender: TObject) ;
2022-03-30 16:48:20 +02:00
procedure FormShow( Sender: TObject) ;
procedure FormCreate( Sender: TObject) ;
procedure FormCloseQuery( Sender: TObject; var CanClose: Boolean ) ;
procedure Chromium1AfterCreated( Sender: TObject; const browser: ICefBrowser) ;
procedure Chromium1BeforeClose( 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, Result : Boolean ) ;
procedure Chromium1OpenUrlFromTab( Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean ; out Result : Boolean ) ;
procedure Chromium1DevToolsMethodResult( Sender: TObject; const browser: ICefBrowser; message_id: Integer ; success: Boolean ; const result : ICefValue) ;
protected
// Variables to control when can we destroy the form safely
FCanClose : boolean ; // Set to True in TChromium.OnBeforeClose
FClosing : boolean ; // Set to True in the CloseQuery event.
2022-04-02 16:32:39 +02:00
FPendingMsgID : integer ;
2022-03-30 16:48:20 +02:00
// You have to handle this two messages to call NotifyMoveOrResizeStarted or some page elements will be misaligned.
procedure WMMove( var aMessage : TWMMove) ; message WM_MOVE;
procedure WMMoving( var aMessage : TMessage) ; message WM_MOVING;
// You also have to handle these two messages to set GlobalCEFApp.OsmodalLoop
procedure WMEnterMenuLoop( var aMessage: TMessage) ; message WM_ENTERMENULOOP;
procedure WMExitMenuLoop( var aMessage: TMessage) ; message WM_EXITMENULOOP;
procedure BrowserCreatedMsg( var aMessage : TMessage) ; message CEF_AFTERCREATED;
procedure HandleSetUserAgentResult( aSuccess : boolean ; const aResult: ICefValue) ;
procedure HandleSetTouchEmulationEnabledResult( aSuccess : boolean ; const aResult: ICefValue) ;
procedure HandleCanEmulateResult( aSuccess : boolean ; const aResult: ICefValue) ;
procedure HandleClearDeviceMetricsOverrideResult( aSuccess : boolean ; const aResult: ICefValue) ;
procedure HandleSetDeviceMetricsOverrideResult( aSuccess : boolean ; const aResult: ICefValue) ;
public
{ Public declarations }
end ;
var
Form1: TForm1;
2024-06-18 14:18:16 +02:00
procedure CreateGlobalCEFApp;
2022-03-30 16:48:20 +02:00
implementation
{$R *.dfm}
uses
2024-04-18 12:06:34 +02:00
uCEFApplication, uCefMiscFunctions, uCEFListValue;
2022-03-30 16:48:20 +02:00
// This demo allows you to emulate a mobile browser using the "Emulation" namespace of the DevTools.
// It's necesary to reload the browser after using the controls in the right panel.
// This demo uses a TChromium and a TCEFWindowParent
// Destruction steps
// =================
2024-09-03 17:26:03 +02:00
// 1. FormCloseQuery sets CanClose to FALSE, destroys CEFWindowParent1 and calls TChromium.CloseBrowser which triggers the TChromium.OnBeforeClose event.
// 2. TChromium.OnBeforeClose sets FCanClose := True and sends WM_CLOSE to the form.
2022-03-30 16:48:20 +02:00
const
DEVTOOLS_SETUSERAGENTOVERRIDE_MSGID = 1 ;
DEVTOOLS_SETTOUCHEMULATIONENABLED_MSGID = 2 ;
DEVTOOLS_CANEMULATE_MSGID = 3 ;
DEVTOOLS_CLEARDEVICEMETRICSOVERRIDE_MSGID = 4 ;
DEVTOOLS_SETDEVICEMETRICSOVERRIDE_MSGID = 5 ;
2024-06-18 14:18:16 +02:00
procedure CreateGlobalCEFApp;
begin
GlobalCEFApp : = TCefApplication. Create;
GlobalCEFApp. cache : = 'cache' ;
GlobalCEFApp. EnablePrintPreview : = True ;
GlobalCEFApp. EnableGPU : = True ;
2024-09-06 10:43:27 +02:00
// Enable this line to set the user agent. This is needed for https://abrahamjuliot.github.io/creepjs/
//GlobalCEFApp.UserAgent := 'Mozilla/5.0 (Linux; Android 11; M2102K1G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36';
2024-06-18 14:18:16 +02:00
{$IFDEF DEBUG}
GlobalCEFApp. LogFile : = 'debug.log' ;
GlobalCEFApp. LogSeverity : = LOGSEVERITY_INFO;
{$ENDIF}
end ;
2022-03-30 16:48:20 +02:00
procedure TForm1. FormCloseQuery( Sender: TObject; var CanClose: Boolean ) ;
begin
CanClose : = FCanClose;
if not( FClosing) then
begin
FClosing : = True ;
Visible : = False ;
Chromium1. CloseBrowser( True ) ;
2024-09-03 17:26:03 +02:00
CEFWindowParent1. Free;
2022-03-30 16:48:20 +02:00
end ;
end ;
procedure TForm1. FormCreate( Sender: TObject) ;
begin
2022-04-02 16:32:39 +02:00
FCanClose : = False ;
FClosing : = False ;
FPendingMsgID : = 0 ;
2024-06-19 11:05:42 +02:00
Chromium1. DefaultURL : = AddressCb. Text ;
2022-03-30 16:48:20 +02:00
end ;
procedure TForm1. FormShow( Sender: TObject) ;
begin
// You *MUST* call CreateBrowser to create and initialize the browser.
// This will trigger the AfterCreated event when the browser is fully
// initialized and ready to receive commands.
// GlobalCEFApp.GlobalContextInitialized has to be TRUE before creating any browser
// If it's not initialized yet, we use a simple timer to create the browser later.
if not( Chromium1. CreateBrowser( CEFWindowParent1) ) then Timer1. Enabled : = True ;
end ;
procedure TForm1. CanEmulateBtnClick( Sender: TObject) ;
begin
2022-04-02 16:32:39 +02:00
FPendingMsgID : = DEVTOOLS_CANEMULATE_MSGID;
Chromium1. ExecuteDevToolsMethod( 0 , 'Emulation.canEmulate' , nil ) ;
2022-03-30 16:48:20 +02:00
end ;
procedure TForm1. Chromium1AfterCreated( Sender: TObject; const browser: ICefBrowser) ;
begin
// Now the browser is fully initialized we can send a message to the main form to load the initial web page.
PostMessage( Handle, CEF_AFTERCREATED, 0 , 0 ) ;
end ;
procedure TForm1. Chromium1BeforeClose( Sender: TObject;
const browser: ICefBrowser) ;
begin
FCanClose : = True ;
PostMessage( Handle, WM_CLOSE, 0 , 0 ) ;
end ;
procedure TForm1. 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, Result : Boolean ) ;
begin
// For simplicity, this demo blocks all popup windows and new tabs
2023-12-15 18:06:46 +01:00
Result : = ( targetDisposition in [ CEF_WOD_NEW_FOREGROUND_TAB, CEF_WOD_NEW_BACKGROUND_TAB, CEF_WOD_NEW_POPUP, CEF_WOD_NEW_WINDOW] ) ;
2022-03-30 16:48:20 +02:00
end ;
procedure TForm1. Chromium1DevToolsMethodResult( Sender: TObject;
const browser: ICefBrowser; message_id: Integer ; success: Boolean ;
const result : ICefValue) ;
begin
2022-04-02 16:32:39 +02:00
case FPendingMsgID of
2022-03-30 16:48:20 +02:00
DEVTOOLS_SETUSERAGENTOVERRIDE_MSGID : HandleSetUserAgentResult( success, result ) ;
DEVTOOLS_SETTOUCHEMULATIONENABLED_MSGID : HandleSetTouchEmulationEnabledResult( success, result ) ;
DEVTOOLS_CANEMULATE_MSGID : HandleCanEmulateResult( success, result ) ;
DEVTOOLS_CLEARDEVICEMETRICSOVERRIDE_MSGID : HandleClearDeviceMetricsOverrideResult( success, result ) ;
DEVTOOLS_SETDEVICEMETRICSOVERRIDE_MSGID : HandleSetDeviceMetricsOverrideResult( success, result ) ;
end ;
end ;
procedure TForm1. Chromium1OpenUrlFromTab( Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame;
const targetUrl: ustring; targetDisposition: TCefWindowOpenDisposition;
userGesture: Boolean ; out Result : Boolean ) ;
begin
// For simplicity, this demo blocks all popup windows and new tabs
2023-12-15 18:06:46 +01:00
Result : = ( targetDisposition in [ CEF_WOD_NEW_FOREGROUND_TAB, CEF_WOD_NEW_BACKGROUND_TAB, CEF_WOD_NEW_POPUP, CEF_WOD_NEW_WINDOW] ) ;
2022-03-30 16:48:20 +02:00
end ;
procedure TForm1. ClearDeviceMetricsOverrideBtnClick( Sender: TObject) ;
begin
2022-04-02 16:32:39 +02:00
FPendingMsgID : = DEVTOOLS_CLEARDEVICEMETRICSOVERRIDE_MSGID;
Chromium1. ExecuteDevToolsMethod( 0 , 'Emulation.clearDeviceMetricsOverride' , nil ) ;
2022-03-30 16:48:20 +02:00
end ;
procedure TForm1. EmulateTouchChkClick( Sender: TObject) ;
var
TempParams : ICefDictionaryValue;
begin
try
TempParams : = TCefDictionaryValueRef. New;
TempParams. SetBool( 'enabled' , EmulateTouchChk. Checked) ;
if EmulateTouchChk. Checked then
TempParams. SetInt( 'maxTouchPoints' , 2 ) ;
2022-04-02 16:32:39 +02:00
FPendingMsgID : = DEVTOOLS_SETTOUCHEMULATIONENABLED_MSGID;
Chromium1. ExecuteDevToolsMethod( 0 , 'Emulation.setTouchEmulationEnabled' , TempParams) ;
2022-03-30 16:48:20 +02:00
finally
TempParams : = nil ;
end ;
end ;
procedure TForm1. BrowserCreatedMsg( var aMessage : TMessage) ;
begin
Caption : = 'Mobile Browser' ;
AddressPnl. Enabled : = True ;
end ;
procedure TForm1. GoBtnClick( Sender: TObject) ;
begin
// This will load the URL in the edit box
2024-06-19 11:05:42 +02:00
Chromium1. LoadURL( AddressCb. Text ) ;
2022-03-30 16:48:20 +02:00
end ;
procedure TForm1. OverrideDeviceMetricsBtnClick( Sender: TObject) ;
var
TempParams, TempDict : ICefDictionaryValue;
TempFormatSettings : TFormatSettings;
TempOrientation : string ;
begin
try
TempParams : = TCefDictionaryValueRef. New;
TempParams. SetInt( 'width' , WidthEdt. Value) ;
TempParams. SetInt( 'height' , HeightEdt. Value) ;
TempFormatSettings : = TFormatSettings. Create;
TempFormatSettings. DecimalSeparator : = '.' ;
TempParams. SetDouble( 'deviceScaleFactor' , StrToFloat( ScaleEdt. Text , TempFormatSettings) ) ;
TempParams. SetBool( 'mobile' , True ) ;
case OrientationCb. ItemIndex of
0 : TempOrientation : = 'portraitPrimary' ;
1 : TempOrientation : = 'portraitSecondary' ;
2 : TempOrientation : = 'landscapePrimary' ;
else TempOrientation : = 'landscapeSecondary' ;
end ;
TempDict : = TCefDictionaryValueRef. New;
TempDict. SetString( 'type' , TempOrientation) ;
TempDict. SetInt( 'angle' , AngleEdt. Value) ;
TempParams. SetDictionary( 'screenOrientation' , TempDict) ;
2022-04-02 16:32:39 +02:00
FPendingMsgID : = DEVTOOLS_SETDEVICEMETRICSOVERRIDE_MSGID;
Chromium1. ExecuteDevToolsMethod( 0 , 'Emulation.setDeviceMetricsOverride' , TempParams) ;
2022-03-30 16:48:20 +02:00
finally
TempDict : = nil ;
TempParams : = nil ;
end ;
end ;
procedure TForm1. OverrideUserAgentBtnClick( Sender: TObject) ;
var
TempParams : ICefDictionaryValue;
2024-04-18 12:06:34 +02:00
TempMetadataDict, TempBrandDict, TempFullVersionDict: ICefDictionaryValue;
TempBrandsArray, TempFullVersionListArray: ICefListValue;
2022-03-30 16:48:20 +02:00
begin
try
TempParams : = TCefDictionaryValueRef. New;
2024-04-18 12:06:34 +02:00
TempMetadataDict : = TCefDictionaryValueRef. New;
TempBrandsArray : = TCefListValueRef. New;
TempFullVersionListArray : = TCefListValueRef. New;
TempBrandsArray. SetSize( 1 ) ;
TempBrandDict : = TCefDictionaryValueRef. New;
TempBrandDict. SetString( 'brand' , 'Chromium' ) ;
TempBrandDict. SetString( 'version' , '91' ) ;
TempBrandsArray. SetDictionary( 0 , TempBrandDict) ;
TempFullVersionListArray. SetSize( 1 ) ;
TempFullVersionDict : = TCefDictionaryValueRef. New;
TempFullVersionDict. SetString( 'brand' , 'Chromium' ) ; //Not:A
TempFullVersionDict. SetString( 'version' , '91.0.4472.114' ) ;
TempFullVersionListArray. SetDictionary( 0 , TempFullVersionDict) ;
TempMetadataDict. SetList( 'brands' , TempBrandsArray) ;
TempMetadataDict. SetList( 'fullVersionList' , TempFullVersionListArray) ;
2024-06-19 11:05:42 +02:00
TempMetadataDict. SetString( 'fullVersion' , '91.0.4472.114' ) ; // Deprecated
2024-04-18 12:06:34 +02:00
TempMetadataDict. SetString( 'platform' , 'Android' ) ; //or Windows
TempMetadataDict. SetString( 'platformVersion' , '12' ) ;
TempMetadataDict. SetString( 'architecture' , 'arm' ) ;
TempMetadataDict. SetString( 'model' , 'SM-F916N' ) ;
TempMetadataDict. SetBool( 'mobile' , true ) ;
TempMetadataDict. SetString( 'bitness' , '32' ) ;
2022-03-30 16:48:20 +02:00
TempParams. SetString( 'userAgent' , UserAgentCb. Text ) ;
2024-04-18 12:06:34 +02:00
// Setting the userAgentMetadata value is optional and can be omited.
// All the values in TempMetadataDict are just an example and they should be customized for each use case.
TempParams. SetDictionary( 'userAgentMetadata' , TempMetadataDict) ;
2022-03-30 16:48:20 +02:00
2022-04-02 16:32:39 +02:00
FPendingMsgID : = DEVTOOLS_SETUSERAGENTOVERRIDE_MSGID;
Chromium1. ExecuteDevToolsMethod( 0 , 'Emulation.setUserAgentOverride' , TempParams) ;
2022-03-30 16:48:20 +02:00
finally
TempParams : = nil ;
end ;
end ;
procedure TForm1. Timer1Timer( Sender: TObject) ;
begin
Timer1. Enabled : = False ;
if not( Chromium1. CreateBrowser( CEFWindowParent1) ) and not( Chromium1. Initialized) then
Timer1. Enabled : = True ;
end ;
procedure TForm1. WMMove( var aMessage : TWMMove) ;
begin
inherited ;
if ( Chromium1 < > nil ) then Chromium1. NotifyMoveOrResizeStarted;
end ;
procedure TForm1. WMMoving( var aMessage : TMessage) ;
begin
inherited ;
if ( Chromium1 < > nil ) then Chromium1. NotifyMoveOrResizeStarted;
end ;
procedure TForm1. WMEnterMenuLoop( var aMessage: TMessage) ;
begin
inherited ;
if ( aMessage. wParam = 0 ) and ( GlobalCEFApp < > nil ) then GlobalCEFApp. OsmodalLoop : = True ;
end ;
procedure TForm1. WMExitMenuLoop( var aMessage: TMessage) ;
begin
inherited ;
if ( aMessage. wParam = 0 ) and ( GlobalCEFApp < > nil ) then GlobalCEFApp. OsmodalLoop : = False ;
end ;
procedure TForm1. HandleSetUserAgentResult( aSuccess : boolean ; const aResult: ICefValue) ;
begin
if aSuccess and ( aResult < > nil ) then
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Successful SetUserAgentOverride' ) ;
end )
else
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Unsuccessful SetUserAgentOverride' ) ;
end ) ;
end ;
procedure TForm1. HandleSetTouchEmulationEnabledResult( aSuccess : boolean ; const aResult: ICefValue) ;
begin
if aSuccess and ( aResult < > nil ) then
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Successful SetTouchEmulationEnabled' ) ;
end )
else
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Unsuccessful SetTouchEmulationEnabled' ) ;
end ) ;
end ;
procedure TForm1. HandleCanEmulateResult( aSuccess : boolean ; const aResult: ICefValue) ;
var
TempRsltDict : ICefDictionaryValue;
TempResult : boolean ;
begin
if aSuccess and ( aResult < > nil ) then
begin
TempRsltDict : = aResult. GetDictionary;
if TCEFJson. ReadBoolean( TempRsltDict, 'result' , TempResult) and TempResult then
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Successful CanEmulate. Emulation is supported.' ) ;
end )
else
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Successful CanEmulate. Emulation is not supported.' ) ;
end ) ;
end
else
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Unsuccessful CanEmulate' ) ;
end ) ;
end ;
procedure TForm1. HandleClearDeviceMetricsOverrideResult( aSuccess : boolean ; const aResult: ICefValue) ;
begin
if aSuccess and ( aResult < > nil ) then
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Successful ClearDeviceMetricsOverride' ) ;
end )
else
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Unsuccessful ClearDeviceMetricsOverride' ) ;
end ) ;
end ;
procedure TForm1. HandleSetDeviceMetricsOverrideResult( aSuccess : boolean ; const aResult: ICefValue) ;
begin
if aSuccess and ( aResult < > nil ) then
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Successful SetDeviceMetricsOverride' ) ;
end )
else
TThread. ForceQueue( nil ,
procedure
begin
LogMem. Lines. Add( 'Unsuccessful SetDeviceMetricsOverride' ) ;
end ) ;
end ;
end .