2021-03-20 20:57:50 +01:00
|
|
|
unit uCEFBrowserWindow;
|
2021-02-17 04:22:11 +01:00
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
{$i cef.inc}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
{$IFDEF FPC}
|
2021-03-20 02:14:13 +01:00
|
|
|
LResources, PropEdits,
|
2021-02-17 04:22:11 +01:00
|
|
|
{$ENDIF}
|
|
|
|
uCEFApplication, uCEFChromiumWindow, uCEFTypes, uCEFInterfaces, uCEFChromium,
|
2021-03-21 01:15:00 +01:00
|
|
|
uCEFLinkedWinControlBase, uCEFBrowserViewComponent,
|
2021-03-20 02:14:13 +01:00
|
|
|
uCEFChromiumEvents, Forms, ExtCtrls, Controls, Classes, sysutils;
|
2021-02-17 04:22:11 +01:00
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
(* On cocoa closing a browser does not work, while the application is in any other event.
|
|
|
|
I.e. if the App is in a button-press event, then the browser will
|
|
|
|
only close once that event was finished.
|
|
|
|
*)
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
{ TEmbeddedChromium
|
2021-03-05 00:14:43 +01:00
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
1) TEmbeddedChromium keeps track of the browser while it is created.
|
|
|
|
This allows for CloseBrowser to function, even if the Browser object is not
|
|
|
|
yet known.
|
|
|
|
Also calls to "LoadUrl" are cached until the browser object is created.
|
|
|
|
|
|
|
|
2) TEmbeddedChromium adds InternalEvents that can be hooked by the
|
|
|
|
component that owns the TEmbeddedChromium.
|
|
|
|
This means the default published events are available to the end user.
|
|
|
|
Published events that should not be available are hidden via THiddenPropertyEditor
|
|
|
|
* Hidden event properties must not be assigned by any end user code. *
|
|
|
|
}
|
|
|
|
|
|
|
|
TEmbeddedChromium = class(TChromium)
|
2021-03-05 00:14:43 +01:00
|
|
|
private type
|
2021-03-20 20:57:50 +01:00
|
|
|
TChromiumBrowserState = (csNoBrowser, csCreatingBrowser, csHasBrowser, csClosingBrowser, csCloseAfterCreate);
|
2021-03-05 00:14:43 +01:00
|
|
|
private
|
2021-03-20 02:14:13 +01:00
|
|
|
FInternalOnGotFocus: TOnGotFocus;
|
2021-03-20 20:57:50 +01:00
|
|
|
FState : TChromiumBrowserState;
|
2021-03-20 02:14:13 +01:00
|
|
|
FInternalOnBrowserClosed : TNotifyEvent;
|
|
|
|
FInternalOnBrowserCreated : TNotifyEvent;
|
2021-03-05 00:14:43 +01:00
|
|
|
|
|
|
|
FLoadUrl, FFrameName : ustring;
|
|
|
|
function GetIsClosing: Boolean;
|
2021-03-20 02:14:13 +01:00
|
|
|
procedure SetInternalOnClose(AValue: TOnClose);
|
2021-03-05 00:14:43 +01:00
|
|
|
|
|
|
|
protected
|
|
|
|
function GetHasBrowser : boolean; reintroduce;
|
|
|
|
|
|
|
|
procedure doOnBeforeClose(const ABrowser: ICefBrowser); override;
|
|
|
|
procedure doOnAfterCreated(const ABrowser: ICefBrowser); override;
|
2021-03-20 02:14:13 +01:00
|
|
|
procedure doOnGotFocus(const Abrowser: ICefBrowser); override;
|
|
|
|
function MustCreateFocusHandler: boolean; override;
|
2021-03-05 00:14:43 +01:00
|
|
|
|
|
|
|
procedure DoCreated(Data: PtrInt);
|
|
|
|
procedure DoOnClosed(Data: PtrInt);
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
|
|
function CreateBrowser(const aBrowserParent: TWinControl = nil;
|
|
|
|
const aWindowName: ustring = ''; const aContext: ICefRequestContext =
|
|
|
|
nil; const aExtraInfo: ICefDictionaryValue = nil): boolean; overload; override;
|
|
|
|
function CreateBrowser(aParentHandle: TCefWindowHandle;
|
|
|
|
aParentRect: TRect; const aWindowName: ustring = '';
|
|
|
|
const aContext: ICefRequestContext = nil;
|
2021-04-18 19:36:20 +02:00
|
|
|
const aExtraInfo: ICefDictionaryValue = nil;
|
|
|
|
aForceAsPopup : boolean = False): boolean; overload; override;
|
2021-03-05 00:14:43 +01:00
|
|
|
function CreateBrowser(const aURL: ustring;
|
|
|
|
const aBrowserViewComp: TCEFBrowserViewComponent;
|
|
|
|
const aContext: ICefRequestContext = nil;
|
|
|
|
const aExtraInfo: ICefDictionaryValue = nil): boolean; overload; override;
|
|
|
|
|
|
|
|
// CloseBrowser will work, even if the browser is still in creation, and Initialized is still false
|
|
|
|
procedure CloseBrowser(aForceClose: boolean); reintroduce;
|
|
|
|
|
|
|
|
// LoadURL will work, even if the browser is still in creation, and Initialized is still false
|
|
|
|
procedure LoadURL(const aURL: ustring; const aFrameName: ustring = ''); overload;
|
|
|
|
|
|
|
|
property HasBrowser: Boolean read GetHasBrowser; // Includes browser in creation
|
|
|
|
property IsClosing : Boolean read GetIsClosing;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
(* - Events for use by the Owning component ONLY
|
|
|
|
- Events are called in main thread
|
2021-03-05 00:14:43 +01:00
|
|
|
- OnBrowserCreated: the parent event may be called when procedure Initialized is still false.
|
|
|
|
- OnBrowserCreated: may not be called, if the CloseBrowser has already been called
|
|
|
|
*)
|
2021-03-20 02:14:13 +01:00
|
|
|
property InternalOnBrowserCreated : TNotifyEvent read FInternalOnBrowserCreated write FInternalOnBrowserCreated;
|
|
|
|
property InternalOnBrowserClosed : TNotifyEvent read FInternalOnBrowserClosed write FInternalOnBrowserClosed;
|
|
|
|
property InternalOnGotFocus : TOnGotFocus read FInternalOnGotFocus write FInternalOnGotFocus;
|
2021-03-05 00:14:43 +01:00
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
TBrowserWindow = class;
|
2021-02-17 04:22:11 +01:00
|
|
|
|
|
|
|
{ TChromiumWrapper }
|
|
|
|
|
|
|
|
TChromiumWrapper = class
|
|
|
|
protected type
|
|
|
|
TWrapperState = (wsNone, wsWaitingForClose, wsSentCloseEventAfterWait, wsDestroyAfterWait);
|
|
|
|
protected
|
2021-03-20 20:57:50 +01:00
|
|
|
FChromium : TEmbeddedChromium;
|
2021-02-17 04:22:11 +01:00
|
|
|
FWrapperState : TWrapperState;
|
2021-03-20 20:57:50 +01:00
|
|
|
FBrowserWindow : TBrowserWindow;
|
2021-02-17 04:22:11 +01:00
|
|
|
|
2021-03-05 00:14:43 +01:00
|
|
|
procedure DoOnAfterCreated(Sender: TObject);
|
|
|
|
procedure DoOnBeforeClose(Sender: TObject);
|
|
|
|
|
|
|
|
procedure BrowserThread_OnClose(Sender: TObject; const browser: ICefBrowser; var aAction : TCefCloseBrowserAction);
|
2021-02-17 04:22:11 +01:00
|
|
|
{$IFDEF FPC}
|
2021-03-05 00:14:43 +01:00
|
|
|
procedure BrowserThread_OnGotFocus(Sender: TObject; const browser: ICefBrowser);
|
2021-02-17 04:22:11 +01:00
|
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
procedure MaybeDestroy;
|
|
|
|
public
|
2021-03-20 20:57:50 +01:00
|
|
|
constructor Create(AOwner: TBrowserWindow); reintroduce;
|
2021-02-17 04:22:11 +01:00
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
|
|
function CreateBrowser: boolean;
|
|
|
|
procedure LoadURL(aURL: ustring);
|
|
|
|
procedure CloseBrowser(aForceClose: boolean);
|
|
|
|
function IsClosed: boolean;
|
|
|
|
(* WaitForBrowserClosed calls ProcessMessages.
|
2021-03-20 20:57:50 +01:00
|
|
|
It therefore is possible that the TBrowserWindow will be destroyed
|
2021-02-17 04:22:11 +01:00
|
|
|
when this method returns.
|
|
|
|
It is the callers responsibility to take any necessary precaution.
|
|
|
|
*)
|
|
|
|
procedure WaitForBrowserClosed;
|
2021-03-05 00:14:43 +01:00
|
|
|
|
2021-03-20 02:14:13 +01:00
|
|
|
published
|
2021-03-20 20:57:50 +01:00
|
|
|
property Chromium: TEmbeddedChromium read FChromium;
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
{ TBrowserWindow
|
|
|
|
|
|
|
|
A simple "drop on the Form" component for an full embedded browser.
|
|
|
|
|
|
|
|
The component handles most events required by CEF.
|
|
|
|
The only additions needed to be made by the User in their code are:
|
|
|
|
|
|
|
|
* Implement TForm.OnCloseQuery
|
|
|
|
CEF must be able to destroy the browser, before the main form is closed.
|
|
|
|
(That is while the Form still has a Handle, and the event loop is still
|
|
|
|
running)
|
|
|
|
It is adviced to do the same for any other form (other than the main form).
|
|
|
|
|
|
|
|
TForm.OnCloseQuery should call (for each TBrowserWindow)
|
|
|
|
TBrowserWindow.CloseBrowser(True);
|
|
|
|
The Form can be allowed to close by setting (checking for all BrowserWindows)
|
|
|
|
CanClose := BrowserWindow.IsClosed;
|
|
|
|
|
|
|
|
On Windows and Linux it is also possible to Destroy the TBrowserWindow.
|
|
|
|
This will wait for the browser to close, and after that the form can be closed.
|
|
|
|
- However, this must be done in OnCloseQuery (or before).
|
|
|
|
- Once TForm.Destroy is called, it is to late. By that time the event loop
|
|
|
|
no longer runs.
|
|
|
|
|
|
|
|
*** IMPORTANT: (MacOS) ***
|
|
|
|
On MacOs CloseBrowser() must be called, and the *event* must be awaited.
|
|
|
|
Neither destroying the component, nor waiting with App.ProcessMessages will
|
|
|
|
work.
|
|
|
|
On MacOS, CEF will not finish until the OnCloseQuery event returned to the
|
|
|
|
main event loop. (Hence ProcessMessage does not work).
|
|
|
|
The same is true for any action taken in OnClick or other event.
|
|
|
|
CEF always waits for any event to return to the main event loop.
|
|
|
|
See also the BrowserWindowEX example how that affect modal forms.
|
|
|
|
|
|
|
|
* Implement TBrowserWindow.OnBrowserClosed
|
|
|
|
If TForm.OnCloseQuery called CloseBrowser, this callback can be used to
|
|
|
|
call Form.Close again (the callback should check if the browser was
|
|
|
|
closed by OnCloseQuery.
|
|
|
|
|
|
|
|
* On Windows:
|
|
|
|
handle the WM_ENTERMENULOOP and WM_EXITMENULOOP, as shown in examples
|
|
|
|
|
|
|
|
* Optional prevent pop-up windows by implementing
|
|
|
|
Chromium.BeforePopup
|
|
|
|
Chromium.OpenUrlFromTab
|
|
|
|
}
|
|
|
|
|
|
|
|
TBrowserWindow = class(TCEFLinkedWinControlBase)
|
2021-02-17 04:22:11 +01:00
|
|
|
private
|
|
|
|
FChromiumWrapper : TChromiumWrapper;
|
|
|
|
|
|
|
|
FOnBrowserClosed : TNotifyEvent;
|
|
|
|
FOnBrowserCreated : TNotifyEvent;
|
|
|
|
FTimer : TTimer;
|
|
|
|
|
|
|
|
procedure DoCreateBrowser(Sender: TObject);
|
2021-03-05 00:14:43 +01:00
|
|
|
procedure DoCreateBrowserAfterContext(Sender: TObject);
|
2021-03-20 20:57:50 +01:00
|
|
|
function GetEmbeddedChromium: TEmbeddedChromium;
|
2021-02-17 04:22:11 +01:00
|
|
|
protected
|
|
|
|
function GetChromium: TChromium; override;
|
|
|
|
procedure DestroyHandle; override;
|
|
|
|
procedure RealizeBounds; override;
|
|
|
|
|
|
|
|
procedure DoEnter; override;
|
|
|
|
procedure DoExit; override;
|
|
|
|
procedure DoOnCreated;
|
|
|
|
procedure DoOnClosed(Data: PtrInt);
|
|
|
|
procedure DoOnFocus(Data: PtrInt);
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
procedure CreateHandle; override;
|
|
|
|
|
|
|
|
procedure CloseBrowser(aForceClose: boolean);
|
|
|
|
procedure WaitForBrowserClosed;
|
|
|
|
function IsClosed: boolean;
|
|
|
|
procedure LoadURL(aURL: ustring);
|
|
|
|
|
|
|
|
published
|
2021-03-20 20:57:50 +01:00
|
|
|
property Chromium: TEmbeddedChromium read GetEmbeddedChromium;
|
2021-02-17 04:22:11 +01:00
|
|
|
|
|
|
|
property OnBrowserCreated : TNotifyEvent read FOnBrowserCreated write FOnBrowserCreated;
|
2021-03-20 20:57:50 +01:00
|
|
|
(* OnBrowserClosed will not be called, if the TBrowserWindow is
|
2021-02-17 04:22:11 +01:00
|
|
|
destroyed/destroying before the browser is closed.
|
|
|
|
*)
|
|
|
|
property OnBrowserClosed : TNotifyEvent read FOnBrowserClosed write FOnBrowserClosed;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{$IFDEF FPC}
|
|
|
|
procedure Register;
|
|
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
{ TEmbeddedChromium }
|
2021-02-17 04:22:11 +01:00
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TEmbeddedChromium.GetIsClosing: Boolean;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
Result := FState in [csCloseAfterCreate, csClosingBrowser];
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.SetInternalOnClose(AValue: TOnClose);
|
2021-03-20 02:14:13 +01:00
|
|
|
begin
|
|
|
|
inherited OnClose := AValue;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TEmbeddedChromium.GetHasBrowser: boolean;
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
Result := (FState <> csNoBrowser) or (inherited GetHasBrowser);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.doOnBeforeClose(const ABrowser: ICefBrowser);
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
inherited doOnBeforeClose(ABrowser);
|
|
|
|
|
|
|
|
FState := csNoBrowser;
|
|
|
|
Application.QueueAsyncCall(@DoOnClosed, 0);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.doOnAfterCreated(const ABrowser: ICefBrowser);
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
inherited doOnAfterCreated(ABrowser);
|
2021-02-17 04:22:11 +01:00
|
|
|
(* We may still be in Chromium.CreateBrowserSync
|
|
|
|
In that case initialization will happen after this event,
|
|
|
|
but before the call to CreateBrowser returns
|
|
|
|
*)
|
|
|
|
Application.QueueAsyncCall(@DoCreated, 0);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.doOnGotFocus(const Abrowser: ICefBrowser);
|
2021-03-20 02:14:13 +01:00
|
|
|
begin
|
|
|
|
inherited doOnGotFocus(Abrowser);
|
|
|
|
if Assigned(FInternalOnGotFocus) then
|
|
|
|
FInternalOnGotFocus(Self, Abrowser);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TEmbeddedChromium.MustCreateFocusHandler: boolean;
|
2021-03-20 02:14:13 +01:00
|
|
|
begin
|
|
|
|
Result := assigned(FInternalOnGotFocus) or
|
|
|
|
inherited MustCreateFocusHandler;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.DoCreated(Data: PtrInt);
|
2021-03-05 00:14:43 +01:00
|
|
|
var
|
|
|
|
u, f: ustring;
|
|
|
|
begin
|
|
|
|
// Any other state, means this is a late async call
|
|
|
|
case FState of
|
|
|
|
csCreatingBrowser: begin
|
|
|
|
FState := csHasBrowser;
|
|
|
|
if FLoadUrl <> '' then begin
|
|
|
|
u := FLoadUrl;
|
|
|
|
f := FFrameName;
|
|
|
|
LoadURL(u, f);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 02:14:13 +01:00
|
|
|
if (FInternalOnBrowserCreated <> nil) then
|
|
|
|
FInternalOnBrowserCreated(Self);
|
2021-03-05 00:14:43 +01:00
|
|
|
end;
|
|
|
|
csCloseAfterCreate: begin
|
|
|
|
FState := csHasBrowser;
|
|
|
|
CloseBrowser(True);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.DoOnClosed(Data: PtrInt);
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
2021-03-20 02:14:13 +01:00
|
|
|
if (FInternalOnBrowserClosed <> nil) then
|
|
|
|
FInternalOnBrowserClosed(Self);
|
2021-03-05 00:14:43 +01:00
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
constructor TEmbeddedChromium.Create(AOwner: TComponent);
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
FState := csNoBrowser;
|
|
|
|
inherited Create(AOwner);
|
2021-03-20 23:06:59 +01:00
|
|
|
SetSubComponent(True);
|
|
|
|
Name := 'Chromium';
|
2021-03-05 00:14:43 +01:00
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
destructor TEmbeddedChromium.Destroy;
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
inherited Destroy;
|
|
|
|
Application.RemoveAsyncCalls(Self);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TEmbeddedChromium.CreateBrowser(const aBrowserParent: TWinControl;
|
2021-03-05 00:14:43 +01:00
|
|
|
const aWindowName: ustring; const aContext: ICefRequestContext;
|
|
|
|
const aExtraInfo: ICefDictionaryValue): boolean;
|
|
|
|
begin
|
|
|
|
FState := csCreatingBrowser;
|
|
|
|
Result := inherited CreateBrowser(aBrowserParent, aWindowName, aContext,
|
|
|
|
aExtraInfo);
|
|
|
|
if Initialized then
|
|
|
|
DoCreated(0);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TEmbeddedChromium.CreateBrowser(aParentHandle: TCefWindowHandle;
|
2021-03-05 00:14:43 +01:00
|
|
|
aParentRect: TRect; const aWindowName: ustring;
|
2021-04-18 19:36:20 +02:00
|
|
|
const aContext: ICefRequestContext; const aExtraInfo: ICefDictionaryValue;
|
|
|
|
aForceAsPopup : boolean): boolean;
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
FState := csCreatingBrowser;
|
|
|
|
Result := inherited CreateBrowser(aParentHandle, aParentRect, aWindowName,
|
2021-04-18 19:36:20 +02:00
|
|
|
aContext, aExtraInfo, aForceAsPopup);
|
2021-03-05 00:14:43 +01:00
|
|
|
if Initialized then
|
|
|
|
DoCreated(0);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TEmbeddedChromium.CreateBrowser(const aURL: ustring;
|
2021-03-05 00:14:43 +01:00
|
|
|
const aBrowserViewComp: TCEFBrowserViewComponent;
|
|
|
|
const aContext: ICefRequestContext; const aExtraInfo: ICefDictionaryValue
|
|
|
|
): boolean;
|
|
|
|
begin
|
|
|
|
FState := csCreatingBrowser;
|
|
|
|
Result := inherited CreateBrowser(aURL, aBrowserViewComp, aContext, aExtraInfo);
|
|
|
|
if Initialized then
|
|
|
|
DoCreated(0);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.CloseBrowser(aForceClose: boolean);
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
if FState = csCreatingBrowser then begin
|
|
|
|
FState := csCloseAfterCreate;
|
|
|
|
exit;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if FState in [csHasBrowser] then
|
|
|
|
begin
|
|
|
|
FState := csClosingBrowser;
|
|
|
|
inherited CloseBrowser(aForceClose);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TEmbeddedChromium.LoadURL(const aURL: ustring; const aFrameName: ustring);
|
2021-03-05 00:14:43 +01:00
|
|
|
begin
|
|
|
|
FLoadUrl := '';
|
|
|
|
FFrameName := '';
|
|
|
|
if FState = csHasBrowser then
|
|
|
|
begin
|
|
|
|
inherited LoadURL(aURL, aFrameName);
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
FLoadUrl := aURL;
|
|
|
|
FFrameName := aFrameName;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TChromiumWrapper }
|
|
|
|
|
|
|
|
procedure TChromiumWrapper.DoOnAfterCreated(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if (FBrowserWindow <> nil) then
|
|
|
|
FBrowserWindow.DoOnCreated;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TChromiumWrapper.BrowserThread_OnClose(Sender: TObject;
|
2021-02-17 04:22:11 +01:00
|
|
|
const browser: ICefBrowser; var aAction: TCefCloseBrowserAction);
|
|
|
|
begin
|
|
|
|
(* FBrowserWindow should always be <> nil
|
|
|
|
If FBrowserWindow is nil (MacOS) then the FBrowserWindow.Handle is destroyed too,
|
|
|
|
and CEF should call BeforeClose, without calling DoClose
|
|
|
|
*)
|
|
|
|
if (FBrowserWindow <> nil) and FBrowserWindow.DestroyChildWindow then
|
|
|
|
aAction := cbaDelay
|
|
|
|
else
|
|
|
|
aAction := cbaClose;
|
|
|
|
end;
|
|
|
|
|
2021-03-05 00:14:43 +01:00
|
|
|
procedure TChromiumWrapper.DoOnBeforeClose(Sender: TObject);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
if (FBrowserWindow <> nil) then begin
|
|
|
|
if FWrapperState = wsWaitingForClose then
|
|
|
|
FWrapperState := wsSentCloseEventAfterWait
|
|
|
|
else
|
2021-03-05 00:14:43 +01:00
|
|
|
FBrowserWindow.DoOnClosed(0);
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-03-05 00:14:43 +01:00
|
|
|
procedure TChromiumWrapper.BrowserThread_OnGotFocus(Sender: TObject;
|
2021-02-17 04:22:11 +01:00
|
|
|
const browser: ICefBrowser);
|
|
|
|
begin
|
|
|
|
if (FBrowserWindow <> nil) then
|
|
|
|
Application.QueueAsyncCall(@FBrowserWindow.DoOnFocus, 0);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TChromiumWrapper.MaybeDestroy;
|
|
|
|
begin
|
2021-03-20 23:06:59 +01:00
|
|
|
if FChromium.Owner <> nil then
|
|
|
|
FBrowserWindow.RemoveComponent(FChromium);
|
2021-02-17 04:22:11 +01:00
|
|
|
CloseBrowser(True);
|
|
|
|
FBrowserWindow := nil;
|
|
|
|
|
|
|
|
if FWrapperState in [wsWaitingForClose, wsSentCloseEventAfterWait] then
|
|
|
|
FWrapperState := wsDestroyAfterWait;
|
|
|
|
|
2021-03-05 00:14:43 +01:00
|
|
|
if not FChromium.HasBrowser then
|
2021-02-17 04:22:11 +01:00
|
|
|
Destroy;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
constructor TChromiumWrapper.Create(AOwner: TBrowserWindow);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
FBrowserWindow := AOwner;
|
|
|
|
FWrapperState := wsNone;
|
|
|
|
|
2021-03-20 23:06:59 +01:00
|
|
|
FChromium := TEmbeddedChromium.Create(AOwner);
|
2021-02-17 04:22:11 +01:00
|
|
|
if not(csDesigning in AOwner.ComponentState) then
|
|
|
|
begin
|
2021-03-20 02:14:13 +01:00
|
|
|
FChromium.OnClose := {$IFDEF FPC}@{$ENDIF}BrowserThread_OnClose;
|
|
|
|
FChromium.InternalOnBrowserClosed := {$IFDEF FPC}@{$ENDIF}DoOnBeforeClose;
|
|
|
|
FChromium.InternalOnBrowserCreated := {$IFDEF FPC}@{$ENDIF}DoOnAfterCreated;
|
2021-02-17 04:22:11 +01:00
|
|
|
{$IFDEF LINUX}
|
|
|
|
// This is a workaround for the CEF issue #2026. Read below for more info.
|
2021-03-20 02:14:13 +01:00
|
|
|
FChromium.InternalOnGotFocus := {$IFDEF FPC}@{$ENDIF}BrowserThread_OnGotFocus;
|
2021-02-17 04:22:11 +01:00
|
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
|
|
|
|
inherited Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TChromiumWrapper.Destroy;
|
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
if FChromium.HasBrowser then
|
2021-02-17 04:22:11 +01:00
|
|
|
WaitForBrowserClosed;
|
|
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
FChromium.Destroy;
|
|
|
|
Application.RemoveAsyncCalls(Self);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TChromiumWrapper.CreateBrowser: boolean;
|
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
if FChromium.HasBrowser then
|
2021-02-17 04:22:11 +01:00
|
|
|
exit(False);
|
|
|
|
|
|
|
|
Result := FChromium.CreateBrowser(FBrowserWindow, '');
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TChromiumWrapper.LoadURL(aURL: ustring);
|
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
FChromium.LoadURL(aURL);
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TChromiumWrapper.CloseBrowser(aForceClose: boolean);
|
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
FChromium.CloseBrowser(aForceClose);
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TChromiumWrapper.IsClosed: boolean;
|
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
Result := not FChromium.HasBrowser;
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TChromiumWrapper.WaitForBrowserClosed;
|
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
if not FChromium.HasBrowser then
|
2021-02-17 04:22:11 +01:00
|
|
|
exit;
|
2021-03-05 00:14:43 +01:00
|
|
|
FChromium.CloseBrowser(True);
|
2021-02-17 04:22:11 +01:00
|
|
|
|
|
|
|
FWrapperState := wsWaitingForClose;
|
2021-03-05 00:14:43 +01:00
|
|
|
while FChromium.HasBrowser do begin
|
2021-02-17 04:22:11 +01:00
|
|
|
Application.ProcessMessages;
|
|
|
|
if GlobalCEFApp.ExternalMessagePump then
|
|
|
|
GlobalCEFApp.DoMessageLoopWork;
|
|
|
|
sleep(5);
|
|
|
|
end;
|
|
|
|
|
|
|
|
if (FBrowserWindow <> nil) and
|
|
|
|
(FWrapperState = wsSentCloseEventAfterWait)
|
|
|
|
then
|
|
|
|
Application.QueueAsyncCall(@FBrowserWindow.DoOnClosed, 0);
|
|
|
|
|
|
|
|
if FWrapperState = wsDestroyAfterWait then
|
|
|
|
Destroy
|
|
|
|
else
|
|
|
|
FWrapperState := wsNone;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
{ TBrowserWindow }
|
2021-02-17 04:22:11 +01:00
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoCreateBrowser(Sender: TObject);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
2021-03-01 20:05:39 +01:00
|
|
|
if FTimer <> nil then
|
|
|
|
FTimer.Enabled := False;
|
2021-02-17 04:22:11 +01:00
|
|
|
|
2021-03-05 00:14:43 +01:00
|
|
|
if FChromiumWrapper.Chromium.HasBrowser then begin
|
|
|
|
if not FChromiumWrapper.Chromium.IsClosing then begin
|
2021-02-17 04:22:11 +01:00
|
|
|
FreeAndNil(FTimer);
|
|
|
|
exit;
|
2021-03-05 00:14:43 +01:00
|
|
|
end
|
|
|
|
else begin
|
2021-02-17 04:22:11 +01:00
|
|
|
FChromiumWrapper.MaybeDestroy;
|
|
|
|
FChromiumWrapper := TChromiumWrapper.Create(Self);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if FChromiumWrapper.CreateBrowser then begin
|
|
|
|
FreeAndNil(FTimer);
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
if GlobalCEFApp.ExternalMessagePump then
|
|
|
|
GlobalCEFApp.DoMessageLoopWork;
|
|
|
|
|
2021-03-01 20:05:39 +01:00
|
|
|
if FTimer = nil then
|
|
|
|
FTimer := TTimer.Create(Self);
|
|
|
|
FTimer.OnTimer := @DoCreateBrowser;
|
2021-02-17 04:22:11 +01:00
|
|
|
FTimer.Interval := 100;
|
|
|
|
FTimer.Enabled := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoCreateBrowserAfterContext(Sender: TObject);
|
2021-03-01 20:05:39 +01:00
|
|
|
begin
|
2021-03-05 00:14:43 +01:00
|
|
|
{$IFnDEF WINDOWS}
|
2021-03-01 20:05:39 +01:00
|
|
|
FTimer := TTimer.Create(Self);
|
|
|
|
FTimer.Interval := 20;
|
|
|
|
FTimer.OnTimer := @DoCreateBrowser;
|
|
|
|
FTimer.Enabled := True;
|
|
|
|
{$ELSE}
|
|
|
|
DoCreateBrowser(nil);
|
|
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TBrowserWindow.GetEmbeddedChromium: TEmbeddedChromium;
|
2021-03-20 02:14:13 +01:00
|
|
|
begin
|
|
|
|
Result := FChromiumWrapper.Chromium;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TBrowserWindow.GetChromium: TChromium;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
Result := FChromiumWrapper.FChromium;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.CreateHandle;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
inherited CreateHandle;
|
|
|
|
if not (csDesigning in ComponentState) then begin
|
|
|
|
(* On Windows we can create the browser immediately.
|
|
|
|
But at least on Linux, we need to wait
|
|
|
|
*)
|
2021-03-01 20:05:39 +01:00
|
|
|
|
2021-03-21 01:15:00 +01:00
|
|
|
GlobalCEFApp.AddContextInitializedHandler(@DoCreateBrowserAfterContext);
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DestroyHandle;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
if FTimer <> nil then
|
|
|
|
FreeAndNil(FTimer);
|
|
|
|
|
|
|
|
if (GlobalCEFApp = nil) or
|
2021-03-05 00:14:43 +01:00
|
|
|
(not FChromiumWrapper.Chromium.HasBrowser) or
|
2021-02-17 04:22:11 +01:00
|
|
|
(csDesigning in ComponentState)
|
|
|
|
then begin
|
|
|
|
inherited DestroyHandle;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{$IFDEF MACOSX}
|
|
|
|
inherited DestroyHandle;
|
|
|
|
FChromiumWrapper.CloseBrowser(True);
|
|
|
|
{$ELSE}
|
|
|
|
FChromiumWrapper.WaitForBrowserClosed;
|
|
|
|
inherited DestroyHandle;
|
|
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.RealizeBounds;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
inherited RealizeBounds;
|
|
|
|
|
|
|
|
if not (csDesigning in ComponentState) and HandleAllocated then
|
|
|
|
Chromium.NotifyMoveOrResizeStarted;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoEnter;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
inherited DoEnter;
|
|
|
|
If not(csDesigning in ComponentState) then Chromium.SetFocus(True);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoExit;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
inherited DoExit;
|
|
|
|
if not(csDesigning in ComponentState) then
|
|
|
|
Chromium.SendCaptureLostEvent;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoOnCreated;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
{$IFDEF FPC}{$IFDEF LINUX}
|
|
|
|
Chromium.UpdateXWindowVisibility(Visible);
|
|
|
|
Chromium.UpdateBrowserSize(Left, Top, Width, Height);
|
|
|
|
{$ENDIF}{$ENDIF}
|
|
|
|
if Assigned(FOnBrowserCreated) then
|
|
|
|
FOnBrowserCreated(Self);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoOnClosed(Data: PtrInt);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
if (not(csDestroying in ComponentState)) and
|
|
|
|
Assigned(FOnBrowserClosed)
|
|
|
|
then
|
|
|
|
FOnBrowserClosed(Self);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.DoOnFocus(Data: PtrInt);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
SetFocus;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
constructor TBrowserWindow.Create(AOwner: TComponent);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
FChromiumWrapper := TChromiumWrapper.Create(Self);
|
|
|
|
inherited Create(AOwner);
|
2021-03-20 23:06:59 +01:00
|
|
|
ControlStyle := ControlStyle + [csOwnedChildrenNotSelectable];
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
destructor TBrowserWindow.Destroy;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
2021-03-20 23:06:59 +01:00
|
|
|
RemoveComponent(FChromiumWrapper.FChromium);
|
2021-02-17 04:22:11 +01:00
|
|
|
inherited Destroy;
|
|
|
|
FChromiumWrapper.MaybeDestroy;
|
|
|
|
Application.RemoveAsyncCalls(Self);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.CloseBrowser(aForceClose: boolean);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
FChromiumWrapper.CloseBrowser(aForceClose);
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.WaitForBrowserClosed;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
FChromiumWrapper.WaitForBrowserClosed;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
function TBrowserWindow.IsClosed: boolean;
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
Result := FChromiumWrapper.IsClosed;
|
|
|
|
end;
|
|
|
|
|
2021-03-20 20:57:50 +01:00
|
|
|
procedure TBrowserWindow.LoadURL(aURL: ustring);
|
2021-02-17 04:22:11 +01:00
|
|
|
begin
|
|
|
|
FChromiumWrapper.LoadURL(aURL);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{$IFDEF FPC}
|
|
|
|
|
|
|
|
procedure Register;
|
|
|
|
begin
|
2021-03-20 20:57:50 +01:00
|
|
|
{$I res/TBrowserWindow.lrs}
|
|
|
|
RegisterComponents('Chromium', [TBrowserWindow]);
|
2021-03-20 23:06:59 +01:00
|
|
|
RegisterClass(TEmbeddedChromium);
|
2021-03-20 20:57:50 +01:00
|
|
|
RegisterPropertyEditor(TypeInfo(TOnClose), TEmbeddedChromium, 'OnClose', THiddenPropertyEditor);
|
2021-02-17 04:22:11 +01:00
|
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|
|
|