CEF4Delphi/demos/Lazarus_Linux_Console/ConsoleBrowser/ucefbrowserthread.pas
2024-06-14 19:17:43 +02:00

444 lines
14 KiB
ObjectPascal

unit ucefbrowserthread;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, SyncObjs,
uworkerthread, ucustommessage,
uCEFChromium, uCEFTypes, uCEFInterfaces, uCEFConstants, uCEFBufferPanel,
uCEFChromiumCore, uCEFMiscFunctions;
type
TThreadStatus = (tsInitializing, tsIdle, tsLoading, tsClosing, tsDestroyed, tsInitError);
TSize = record
cx : integer;
cy : integer;
end;
TCEFBrowserThread = class(TWorkerThread)
protected
FBrowser : TChromium;
FStatus : TThreadStatus;
FBrowserSize : TSize;
FBrowserCS : TCriticalSection;
FErrorCode : integer;
FErrorText : string;
FFailedURL : string;
FDefaultURL : string;
FFileName : string;
FMessageID : integer;
FOnInitialized : TNotifyEvent;
FOnSnapshotAvailable : TNotifyEvent;
FOnError : TNotifyEvent;
function GetErrorCode : integer;
function GetErrorText : string;
function GetFailedURL : string;
function GetInitialized : boolean;
function GetClosing : boolean;
function GetStatus : TThreadStatus;
function GetFileName : string;
procedure SetErrorText(const aValue : string);
procedure SetFileName(const aValue : string);
procedure SetStatus(aValue : TThreadStatus);
procedure Browser_OnAfterCreated(Sender: TObject; const browser: ICefBrowser);
procedure Browser_OnGetViewRect(Sender: TObject; const browser: ICefBrowser; var rect: TCefRect);
procedure Browser_OnGetScreenInfo(Sender: TObject; const browser: ICefBrowser; var screenInfo: TCefScreenInfo; out Result: Boolean);
procedure Browser_OnBeforePopup(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 Browser_OnBeforeClose(Sender: TObject; const browser: ICefBrowser);
procedure Browser_OnLoadError(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; errorCode: TCefErrorcode; const errorText, failedUrl: ustring);
procedure Browser_OnLoadingStateChange(Sender: TObject; const browser: ICefBrowser; isLoading, canGoBack, canGoForward: Boolean);
procedure Browser_OnDevToolsMethodResult(Sender: TObject; const browser: ICefBrowser; message_id: Integer; success: Boolean; const result: ICefValue);
procedure Browser_OnOpenUrlFromTab(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const targetUrl: ustring; targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean; out Result: Boolean);
procedure DoOnInitialized;
procedure DoOnError;
procedure DoOnSnapshotAvailable;
procedure ProcessValue(const aInfo : TMsgInfo); override;
procedure DoLoadURL(const aURL : string);
function CreateBrowser : boolean;
procedure CloseBrowser;
procedure InitError;
procedure Execute; override;
public
constructor Create(aWidth, aHeight : integer; const aDefaultURL, aFileName : string);
destructor Destroy; override;
procedure AfterConstruction; override;
procedure TerminateBrowserThread;
procedure LoadURL(const aURL : string);
property ErrorCode : integer read GetErrorCode;
property ErrorText : string read GetErrorText write SetErrorText;
property FailedUrl : string read GetFailedUrl;
property Initialized : boolean read GetInitialized;
property Closing : boolean read GetClosing;
property Status : TThreadStatus read GetStatus write SetStatus;
property FileName : string read GetFileName write SetFileName;
property OnInitialized : TNotifyEvent read FOnInitialized write FOnInitialized;
property OnSnapshotAvailable : TNotifyEvent read FOnSnapshotAvailable write FOnSnapshotAvailable;
property OnError : TNotifyEvent read FOnError write FOnError;
end;
implementation
uses
uCEFDictionaryValue, uCEFJson;
const
WORKERTHREADMSG_LOADURL = WORKERTHREADMSG_QUIT + 1;
WORKERTHREADMSG_DOONERROR = WORKERTHREADMSG_QUIT + 2;
WORKERTHREADMSG_CLOSEBROWSER = WORKERTHREADMSG_QUIT + 3;
constructor TCEFBrowserThread.Create(aWidth, aHeight : integer; const aDefaultURL, aFileName : string);
begin
inherited Create;
FStatus := tsInitializing;
FBrowser := nil;
FBrowserSize.cx := aWidth;
FBrowserSize.cy := aHeight;
FDefaultURL := aDefaultURL;
FFileName := aFileName;
FBrowserCS := nil;
FMessageID := -1;
FOnInitialized := nil;
FOnSnapshotAvailable := nil;
FOnError := nil;
end;
destructor TCEFBrowserThread.Destroy;
begin
if (FBrowser <> nil) then
FreeAndNil(FBrowser);
if (FBrowserCS <> nil) then
FreeAndNil(FBrowserCS);
inherited Destroy;
end;
procedure TCEFBrowserThread.AfterConstruction;
begin
inherited AfterConstruction;
FBrowserCS := TCriticalSection.Create;
FBrowser := TChromium.Create(nil);
FBrowser.DefaultURL := FDefaultURL;
FBrowser.Options.BackgroundColor := CefColorSetARGB($FF, $FF, $FF, $FF);
FBrowser.OnAfterCreated := @Browser_OnAfterCreated;
FBrowser.OnGetViewRect := @Browser_OnGetViewRect;
FBrowser.OnGetScreenInfo := @Browser_OnGetScreenInfo;
FBrowser.OnBeforePopup := @Browser_OnBeforePopup;
FBrowser.OnBeforeClose := @Browser_OnBeforeClose;
FBrowser.OnLoadError := @Browser_OnLoadError;
FBrowser.OnLoadingStateChange := @Browser_OnLoadingStateChange;
FBrowser.OnOpenUrlFromTab := @Browser_OnOpenUrlFromTab;
FBrowser.OnDevToolsMethodResult := @Browser_OnDevToolsMethodResult;
end;
procedure TCEFBrowserThread.TerminateBrowserThread;
begin
Terminate;
EnqueueMessage(WORKERTHREADMSG_CLOSEBROWSER);
end;
procedure TCEFBrowserThread.LoadURL(const aURL : string);
begin
EnqueueMessage(WORKERTHREADMSG_LOADURL, 0, aURL);
end;
function TCEFBrowserThread.GetErrorCode : integer;
begin
FBrowserCS.Acquire;
Result := FErrorCode;
FBrowserCS.Release;
end;
function TCEFBrowserThread.GetErrorText : string;
begin
FBrowserCS.Acquire;
Result := FErrorText;
FBrowserCS.Release;
end;
function TCEFBrowserThread.GetFailedURL : string;
begin
FBrowserCS.Acquire;
Result := FFailedURL;
FBrowserCS.Release;
end;
function TCEFBrowserThread.GetInitialized : boolean;
begin
FBrowserCS.Acquire;
Result := not(Terminated) and (FStatus in [tsIdle, tsLoading]);
FBrowserCS.Release;
end;
function TCEFBrowserThread.GetClosing : boolean;
begin
FBrowserCS.Acquire;
Result := (FStatus = tsClosing);
FBrowserCS.Release;
end;
function TCEFBrowserThread.GetStatus : TThreadStatus;
begin
FBrowserCS.Acquire;
Result := FStatus;
FBrowserCS.Release;
end;
function TCEFBrowserThread.GetFileName : string;
begin
FBrowserCS.Acquire;
Result := FFileName;
FBrowserCS.Release;
end;
procedure TCEFBrowserThread.SetErrorText(const aValue : string);
begin
FBrowserCS.Acquire;
FErrorText := aValue;
FBrowserCS.Release;
end;
procedure TCEFBrowserThread.SetFileName(const aValue : string);
begin
FBrowserCS.Acquire;
FFileName := aValue;
FBrowserCS.Release;
end;
procedure TCEFBrowserThread.SetStatus(aValue : TThreadStatus);
begin
FBrowserCS.Acquire;
FStatus := aValue;
FBrowserCS.Release;
end;
procedure TCEFBrowserThread.DoOnInitialized;
begin
if assigned(FOnInitialized) then
FOnInitialized(self);
end;
procedure TCEFBrowserThread.DoOnError;
begin
if assigned(FOnError) then
FOnError(self);
end;
procedure TCEFBrowserThread.DoOnSnapshotAvailable;
begin
if assigned(FOnSnapshotAvailable) then
FOnSnapshotAvailable(self);
end;
procedure TCEFBrowserThread.Browser_OnAfterCreated(Sender: TObject;
const browser: ICefBrowser);
begin
Status := tsIdle;
DoOnInitialized;
end;
procedure TCEFBrowserThread.Browser_OnGetViewRect(Sender: TObject;
const browser: ICefBrowser; var rect: TCefRect);
begin
rect.x := 0;
rect.y := 0;
rect.width := FBrowserSize.cx;
rect.height := FBrowserSize.cy;
end;
procedure TCEFBrowserThread.Browser_OnGetScreenInfo(Sender: TObject;
const browser: ICefBrowser; var screenInfo: TCefScreenInfo; out Result: Boolean);
var
TempRect : TCEFRect;
begin
TempRect.x := 0;
TempRect.y := 0;
TempRect.width := FBrowserSize.cx;
TempRect.height := FBrowserSize.cy;
screenInfo.device_scale_factor := 1;
screenInfo.depth := 0;
screenInfo.depth_per_component := 0;
screenInfo.is_monochrome := Ord(False);
screenInfo.rect := TempRect;
screenInfo.available_rect := TempRect;
Result := True;
end;
procedure TCEFBrowserThread.Browser_OnBeforePopup(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);
begin
Result := (targetDisposition in [CEF_WOD_NEW_FOREGROUND_TAB,
CEF_WOD_NEW_BACKGROUND_TAB,
CEF_WOD_NEW_POPUP,
CEF_WOD_NEW_WINDOW]);
end;
procedure TCEFBrowserThread.Browser_OnBeforeClose(Sender: TObject; const browser: ICefBrowser);
begin
Status := tsDestroyed;
EnqueueMessage(WORKERTHREADMSG_QUIT);
end;
procedure TCEFBrowserThread.Browser_OnLoadError(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame; errorCode: TCefErrorcode;
const errorText, failedUrl: ustring);
begin
if not(Terminated) and (frame <> nil) and frame.IsValid and frame.IsMain then
try
FBrowserCS.Acquire;
FErrorCode := errorCode;
FErrorText := errorText;
FFailedUrl := failedUrl;
finally
FBrowserCS.Release;
EnqueueMessage(WORKERTHREADMSG_DOONERROR);
end;
end;
procedure TCEFBrowserThread.Browser_OnLoadingStateChange(Sender: TObject;
const browser: ICefBrowser; isLoading, canGoBack, canGoForward: Boolean);
var
TempParams : ICefDictionaryValue;
begin
if isLoading then
Status := tsLoading
else
begin
Status := tsIdle;
TempParams := TCefDictionaryValueRef.New;
TempParams.SetString('format', 'png');
FMessageID := FBrowser.ExecuteDevToolsMethod(0, 'Page.captureScreenshot', TempParams);
TempParams := nil;
end;
end;
procedure TCEFBrowserThread.Browser_OnDevToolsMethodResult(Sender: TObject;
const browser: ICefBrowser; message_id: Integer; success: Boolean;
const result: ICefValue);
var
TempRsltDict : ICefDictionaryValue;
TempString : ustring;
TempBin : ICefBinaryValue;
TempStream : TFileStream;
TempSuccess : boolean;
begin
if not(success) or (FMessageID <> message_id) or not(assigned(result)) then exit;
TempSuccess := False;
TempStream := nil;
TempRsltDict := result.GetDictionary;
if assigned(TempRsltDict) then
try
if TCEFJson.ReadString(TempRsltDict, 'data', TempString) then
try
TempBin := CefBase64Decode(TempString);
if assigned(TempBin) and (TempBin.Size > 0) then
try
try
TempStream := TFileStream.Create(FileName, fmCreate);
TempStream.WriteBuffer(TempBin.GetRawData^, TempBin.Size);
TempSuccess := True;
except
on e : exception do
if CustomExceptionHandler('TCEFBrowserThread.Browser_OnDevToolsMethodResult', e) then raise;
end;
finally
if assigned(TempStream) then
FreeAndNil(TempStream);
end;
finally
TempBin := nil;
end;
finally
TempRsltDict := nil;
if TempSuccess then DoOnSnapshotAvailable;
end;
end;
procedure TCEFBrowserThread.Browser_OnOpenUrlFromTab(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl: ustring;
targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean;
out Result: Boolean);
begin
Result := (targetDisposition in [CEF_WOD_NEW_FOREGROUND_TAB,
CEF_WOD_NEW_BACKGROUND_TAB,
CEF_WOD_NEW_POPUP,
CEF_WOD_NEW_WINDOW]);
end;
procedure TCEFBrowserThread.ProcessValue(const aInfo : TMsgInfo);
begin
case aInfo.Msg of
WORKERTHREADMSG_LOADURL : DoLoadURL(aInfo.StrParam);
WORKERTHREADMSG_DOONERROR : DoOnError;
WORKERTHREADMSG_CLOSEBROWSER : CloseBrowser;
end;
end;
function TCEFBrowserThread.CreateBrowser : boolean;
begin
Result := assigned(FBrowser) and FBrowser.CreateBrowser;
end;
procedure TCEFBrowserThread.DoLoadURL(const aURL : string);
begin
if not(Terminated) and Initialized and assigned(FBrowser) then
FBrowser.LoadURL(aURL);
end;
procedure TCEFBrowserThread.CloseBrowser;
begin
if Initialized then
begin
if assigned(FBrowser) then
begin
Status := tsClosing;
FBrowser.CloseBrowser(True);
end;
end
else
if not(Closing) then
EnqueueMessage(WORKERTHREADMSG_QUIT);
end;
procedure TCEFBrowserThread.InitError;
begin
Status := tsInitError;
ErrorText := 'There was an error initializing the CEF browser.';
DoOnError;
end;
procedure TCEFBrowserThread.Execute;
begin
if CreateBrowser then
inherited Execute
else
InitError;
end;
end.