{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Author:       Angus Robertson, Magenta Systems Ltd
Description:  OAuth authentication browser window VCL form.
Creation:     Dec 2022
Updated:      Aug 2026
Version:      V9.8 -- 10.4 and up only
EMail:        francois.piette@overbyte.be  http://www.overbyte.be
Support:      https://en.delphipraxis.net/forum/37-ics-internet-component-suite/
Legal issues: Copyright (C) 2026 by Angus Robertson, Magenta Systems Ltd,
              Croydon, England. delphi@magsys.co.uk, https://www.magsys.co.uk/delphi/

              This software is provided 'as-is', without any express or
              implied warranty.  In no event will the author be held liable
              for any  damages arising from the use of this software.

              Permission is granted to anyone to use this software for any
              purpose, including commercial applications, and to alter it
              and redistribute it freely, subject to the following
              restrictions:

              1. The origin of this software must not be misrepresented,
                 you must not claim that you wrote the original software.
                 If you use this software in a product, an acknowledgment
                 in the product documentation would be appreciated but is
                 not required.

              2. Altered source versions must be plainly marked as such, and
                 must not be misrepresented as being the original software.

              3. This notice may not be removed or altered from any source
                 distribution.

              4. You must register this software by sending a picture postcard
                 to the author. Use a nice stamp and mention your name, street
                 address, EMail address and any comment you like to say.

Updates:
Dec 14, 2022 - V8.71 - baseline.
Aug 08, 2023 V9.0  Updated version to major release 9.
Feb 23, 2024 V9.1  Builds without USE_SSL.
Aug 21, 2026 V9.8  Copied from OverbyteIcsOAuthFormVcl as OverbyteIcsOAuthFormVclE
                     removing all conditionals so only builds on D10.4 and later,
                     adding components to the form. which can now be opened in
                     IDE without corruption.
                   The samples ideally use $IFDEF COMPILER27_UP to use this unit.
                   Warning, not tested old TWebBrowser for a long time, only
                     now supported by legacy Microsoft services.



This unit displays browser pages to handle OAuth login to cloud services, rather
than using an external browser.

It supports two browser engines with Delphi 10.4 and later, TEdgeBrowser Chromium
based browser introduced in 2020 for Windows 10 and 11 which should be on most
recent PCs provided Windows Update is used, and the older TWebBrowser using
Microsoft's Shell Doc Object and Control Library (SHDOCVW.DLL), part of Internet
Explorer, which is removed when Edge is installed, but still seems to be available,
and is used on older Windows versions.  Edge Chromium can be installed on Windows
7 and later. The unit checks for Edge in the registry and for the WebView2Loader.dll,
otherwise it uses TWebBrowser.

Beware the properties of TWebBrowser changed in Delphi XE2 so a lot of conditions here,
and onShowScriptError added much later. The 11.2 version has SelectedEngine property
to support Edge, but we ignore that and use separate EdgeBrowser.

Note Google no longer supports authentication using TWebBrowser, you will get
script errors and a warning to use another browser, and announced it would no
longer support embedded browsers atall, but a year later Edge still seems to work:
https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html

Officially the Microsoft.Web.WebView2 runtime (from GetIt) must be installed for Edge
Chromium to work, but in practice copying WebView2Loader.dll into the same directory as
the executable seems to work, there are Win32 and Win64 versions of this DLL with the
same name, you need the correct version for the build!







}

unit OverbyteIcsOAuthFormVclE;

{$I Include\OverbyteIcsDefs.inc}

{$IFNDEF COMPILER27_UP}
  'OverbyteIcsOAuthFormVclE is only supported for Delphi 10.4 and later'
{$ENDIF}

{$B-}           { Enable partial boolean evaluation   }
{$T-}           { Untyped pointers                    }
{$X+}           { Enable extended syntax              }
{$H+}           { Use long strings                    }
{$J+}           { Allow typed constant to be modified }
{$Ifdef No_Extended_Rtti}
    {$Rtti Explicit Methods([]) Fields([]) Properties([])}
{$Endif}
{ These Are Usefull For Debugging !}
{$Warn Implicit_String_Cast       Off}
{$Warn Implicit_String_Cast_Loss  On}
{$Warn Explicit_String_Cast       Off}
{$Warn Explicit_String_Cast_Loss  Off}
{$Warn Symbol_Platform   Off}
{$Warn Symbol_Library    Off}
{$Warn Symbol_Deprecated Off}
{$Define Use_Buffered_Stream}
{$IFDEF BCB3_UP}
    {$ObjExportAll On}
{$ENDIF}

interface

{$IFDEF USE_SSL}

uses
 System.SysUtils,
 System.Classes,
 System.Variants,
 System.IOUtils,
 System.Win.Registry,
 Winapi.Windows,
 Winapi.ActiveX,
 Winapi.WebView2,
 Vcl.Forms,
 Vcl.StdCtrls,
 Vcl.ExtCtrls,
 Vcl.Controls,
 Vcl.Clipbrd,
 Vcl.OleCtrls,
 Vcl.Edge,
 SHDocVw,
 OverbyteIcsWndControl,
 OverbyteIcsSslHttpOAuth;

type

  TOAuthFormLogEvent = procedure(const Line: String) of object;
  TOAuthFormRedirEvent = procedure(const NewUrl: String; var HtmlBody: String; var CanClose: Boolean) of object;

  TOAuthBrowser = class(TIcsWndControl)
  private
  public
    function     BrowserCheckEmbedded: Boolean;
    function     BrowserEmbedded(RestOAuth: TRestOAuth; Owner: TComponent; const URL: String): Boolean;
    destructor   Destroy; override;
  end;

  TOAuthLoginFormE = class(TForm)
    Panel1: TPanel;
    LabelTitle: TLabel;
    doClose: TButton;
    TimerClose: TTimer;
    LabelAccount: TLabel;
    PanelClient: TPanel;
    PanelWebBrowser: TPanel;
    WebBrowser: TWebBrowser;
    PanelEdgeBrowser: TPanel;
    EdgeBrowser: TEdgeBrowser;
    procedure EdgeBrowserFrameNavigationCompleted(Sender: TCustomEdgeBrowser; IsSuccess: Boolean; WebErrorStatus: TOleEnum);
    procedure EdgeBrowserCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
    procedure EdgeBrowserWindowCloseRequested(Sender: TObject);
    procedure EdgeBrowserNavigationStarting(Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
    procedure EdgeBrowserDocumentTitleChanged(Sender: TCustomEdgeBrowser; const ADocumentTitle: string);
    procedure EdgeBrowserSourceChanged(Sender: TCustomEdgeBrowser; IsNewDocument: Boolean);
    procedure EdgeBrowserProcessFailed(Sender: TCustomEdgeBrowser; ProcessFailedKind: TOleEnum);
    procedure EdgeBrowserWebResourceRequested(Sender: TCustomEdgeBrowser; Args: TWebResourceRequestedEventArgs);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure doCloseClick(Sender: TObject);
    procedure Log(Line: String);
    procedure TimerCloseTimer(Sender: TObject);
    procedure WebBrowserTitleChange(ASender: TObject; const Text: WideString);
    procedure WebBrowserBeforeNavigate2(ASender: TObject; const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);
    procedure WebBrowserNavigateComplete2(ASender: TObject; const pDisp: IDispatch; const URL: OleVariant);
    procedure WebBrowserNavigateError(ASender: TObject; const pDisp: IDispatch; const URL, Frame, StatusCode: OleVariant; var Cancel: WordBool);
    procedure WebBrowserShowScriptError(ASender: TObject; const AErrorLine, AErrorCharacter, AErrorMessage, AErrorCode, AErrorUrl: OleVariant; var AOut: OleVariant; var AHandled: Boolean);
  private
    { Private declarations }
//    FEdgeBrowser: TEdgeBrowser;
    FAuthURL: String;
    FRedirDone: Boolean;
    FonLogEvent: TOAuthFormLogEvent;
    FonRedirEvent: TOAuthFormRedirEvent;
    procedure HandleWBRedirect;
  public
    { Public declarations }
    NewURL: String;
    NewTitle: String;
    function GotoURL(const URL, FormTitle, LoginHint, CacheDir: String; IgnoreEdge: Boolean = False): Boolean;
    property onLogEvent: TOAuthFormLogEvent        read FonLogEvent    write FonLogEvent;
    property onRedirEvent: TOAuthFormRedirEvent    read FonRedirEvent  write FonRedirEvent;
  end;

var
  OAuthLoginFormE: TOAuthLoginFormE;
  OALoginFormShowing: Boolean;

const
  MSEdgeVer = '\Software\Microsoft\Edge\BLBeacon';

  function IcsCheckEdgeBrowser (var ErrInfo: String): Boolean;

{$ENDIF USE_SSL}

implementation

{$R *.dfm}

{$IFDEF USE_SSL}

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ try and check if Edge is available before creating the form }
function IcsCheckEdgeBrowser (var ErrInfo: String): Boolean;
var
    EdgeVer: String;
begin
    Result := False;
    ErrInfo := '';

// see if Edge Chromium Browser is installed
    with TRegistry.Create do
    try
        RootKey := HKEY_CURRENT_USER;
        if NOT OpenKeyReadOnly(MSEdgeVer) then begin
            ErrInfo := 'Edge Chromium Browser Not Found';
            Exit;
        end;
        EdgeVer := ReadString('version');
        if EdgeVer = '' then begin
            ErrInfo := 'Edge Chromium Browser Not Found';
            Exit;
         // pending, do we need to check actual version of legacy or chromium browser??
         // currently version=107.0.1418.42
        end;
    finally
        Free;
    end;

// and for the loader DLL we should have installed
// pending, is it Win32 or Win64 to match our build?
    if NOT FileExists('WebView2Loader.dll') then begin
        ErrInfo := 'WebView2Loader.dll Not Found';
        Exit;
    end;
    Result := True;
end;

procedure TOAuthLoginFormE.Log(Line: String);
begin
    if Assigned(FonLogEvent) then
        FonLogEvent(Line);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.TimerCloseTimer(Sender: TObject);
begin
    TimerClose.Enabled := False;
    Log('OAuth Login Form Closing on Timeout');
    Self.Close;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.FormCreate(Sender: TObject);
begin
    OALoginFormShowing := True;
    FRedirDone := False;
    ModalResult := mrCancel;
//    WebBrowser := Nil;
//    FEdgeBrowser := Nil;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    OALoginFormShowing := False;
{    if Assigned(FEdgeBrowser) then begin
        FEdgeBrowser.Free;
        FEdgeBrowser := Nil;
    end;  }
    Action := TCloseAction.caFree;
{    if Assigned(WebBrowser) then begin
        WebBrowser.Free;
        WebBrowser := Nil;
    end;    }
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// call this before showing the form
function TOAuthLoginFormE.GotoURL(const URL, FormTitle, LoginHint, CacheDir: String; IgnoreEdge: Boolean = False): Boolean;
var
    ErrInfo, CacheFolder: String;
begin
    Result := False;
    if NOT IgnoreEdge then begin
        if NOT IcsCheckEdgeBrowser(ErrInfo) then begin
            Log(ErrInfo);
            IgnoreEdge := True;
        end;
        CacheFolder := CacheDir;
        if Pos(':', CacheFolder) = 0 then
                CacheFolder := TPath.GetTempPath + '\ics-edgecache';
        if NOT ForceDirectories(CacheFolder) then begin
            Log('Cache Directory Not Found: ' + CacheFolder);
            Log('Unable to use Edge Browser');
            IgnoreEdge := True;
       end;
    end;
    FAuthURL := URL;

 // see if user has passed an email address or login name hint
    if LoginHint<> '' then begin
        Clipboard.SetTextBuf(PChar(LoginHint));   // copy login name to clipboard
        LabelAccount.Caption := 'Login Hint: ' + LoginHint + ' (copied to clipboard)';
    end
    else
        LabelAccount.Caption := '';
    if FormTitle <> '' then
        Self.Caption := Self.Caption + ' - ' + FormTitle;
    Log(Self.Caption);
    Log(URL);

 // display Edge Browser
    if NOT IgnoreEdge then begin
        Log('Using EdgeBrowser');
        PanelWebBrowser.Visible := False;
        PanelEdgeBrowser.Align := alClient;
        PanelEdgeBrowser.Visible := True;
//        FEdgeBrowser := TEdgeBrowser.Create(Self);
        EdgeBrowser.Parent := PanelClient;
        EdgeBrowser.Left := PanelClient.Left;
        EdgeBrowser.Top := PanelClient.Top;
        EdgeBrowser.Width := PanelClient.Width;
        EdgeBrowser.Height := PanelClient.Height;
        EdgeBrowser.Align := alClient;
{$IFDEF COMPILER28_UP}
        EdgeBrowser.UserDataFolder := CacheFolder;    // Delphi 11 and later
{$ENDIF}
        EdgeBrowser.OnCreateWebViewCompleted := EdgeBrowserCreateWebViewCompleted;
        EdgeBrowser.OnDocumentTitleChanged := EdgeBrowserDocumentTitleChanged;
        EdgeBrowser.OnFrameNavigationCompleted := EdgeBrowserFrameNavigationCompleted;
        EdgeBrowser.OnNavigationStarting := EdgeBrowserNavigationStarting;
        EdgeBrowser.OnProcessFailed := EdgeBrowserProcessFailed;
        EdgeBrowser.OnSourceChanged := EdgeBrowserSourceChanged;
        EdgeBrowser.OnWebResourceRequested := EdgeBrowserWebResourceRequested;
        EdgeBrowser.OnWindowCloseRequested := EdgeBrowserWindowCloseRequested;
        try
            EdgeBrowser.CreateWebView;
            Log('Creating EdgeBrowser WebView');
        // wait 10 secs for browser to be created, or assume dead
            TimerClose.Interval := 10 * 1000;
            TimerClose.Enabled := True;
            Result := True;
        except
            on E:Exception do
                Log('Exception EdgeBrowser WebView - ' + E.Message);
        end;
    end

 // display MSIE TWebBrowser
    else begin
        Log('Using MSIE WebBrowser');
        PanelEdgeBrowser.Visible := False;
        PanelWebBrowser.Align := alClient;
        PanelWebBrowser.Visible := True;
     //   WebBrowser := TWebBrowser.Create(Self);
      //  WebBrowser.Parent := PanelClient;
        WebBrowser.Left := PanelClient.Left;
        WebBrowser.Top := PanelClient.Top;
        WebBrowser.Width := PanelClient.Width;
        WebBrowser.Height := PanelClient.Height;
        WebBrowser.OnTitleChange := WebBrowserTitleChange;
        WebBrowser.OnBeforeNavigate2 := WebBrowserBeforeNavigate2;
        WebBrowser.OnNavigateComplete2 := WebBrowserNavigateComplete2;
        WebBrowser.OnNavigateError := WebBrowserNavigateError;
        WebBrowser.onShowScriptError := WebBrowserShowScriptError;    // Delphi 10.4, stops pop-up error window
       // 11.2 also has SelectedEngine property to support Edge, but we use separate edgebrowser component
        try
            WebBrowser.Navigate(URL);
            Log('Creating MSIE WebBrowser');
            Result := True;
        except
            Log('Exception MSIE WebBrowser');
        end;
    end;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.doCloseClick(Sender: TObject);
begin
    ModalResult := mrCancel;
    Self.Close;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// following events from TEdgeBrowser using Chrome engine, Windows 10 and 11 only with updates
procedure TOAuthLoginFormE.EdgeBrowserCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
begin
    TimerClose.Enabled := False;
    if Succeeded(AResult) then begin
        Log('EdgeBrowser WebView Created');
        if NOT EdgeBrowser.Navigate(FAuthURL) then
            Log('EdgeBrowser Navigate Failed to ' + FAuthURL);
    end
    else begin
        if AResult = HResultFromWin32(ERROR_FILE_NOT_FOUND) then
            Log('EdgeBrowser Not Available')
        else
            Log('EdgeBrowser Failed to Create WebView');
        ModalResult := mrCancel;
        Self.Close;
    end;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// new page loading
procedure TOAuthLoginFormE.EdgeBrowserDocumentTitleChanged(Sender: TCustomEdgeBrowser; const ADocumentTitle: string);
begin
    NewTitle := ADocumentTitle;
    LabelTitle.Caption := NewTitle;
    Log('EdgeBrowser Title: ' + NewTitle);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// new page loaded
procedure TOAuthLoginFormE.EdgeBrowserFrameNavigationCompleted(Sender: TCustomEdgeBrowser; IsSuccess: Boolean; WebErrorStatus: TOleEnum);
begin
   if IsSuccess then
        Log('EdgeBrowser Navigate OK to ' + Sender.LocationURL)
   else
        Log('EdgeBrowser Navigate Failed to ' + Sender.LocationURL);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// event called before page loading starts, check URL for redirect with authentication code argument
procedure TOAuthLoginFormE.EdgeBrowserNavigationStarting(Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
var
    Url: PChar;
    CanCancel: Boolean;
    HtmlBody: String;
begin
    Args.ArgsInterface.Get_uri(Url);   // hard way to get the URL to which navigation is starting
    NewURL := Url;
    CoTaskMemFree(Url);
    Log('EdgeBrowser Navigate Starting to: ' + NewURL);
    if Assigned(FonRedirEvent) then begin
        CanCancel := False;
        HtmlBody := '';
        FonRedirEvent(NewURL, HtmlBody, CanCancel);
        if CanCancel then begin
            Log('EdgeBrowser Authenication Completed Successfully');
            FRedirDone := True;
            Args.ArgsInterface.Set_Cancel(Integer(LongBool(True)));  // try and stop redirection
            ModalResult := mrOk;
            Self.Close;
        end
        else begin
            if HtmlBody <> '' then begin
                FRedirDone := True;
                Args.ArgsInterface.Set_Cancel(Integer(LongBool(True)));  // try and stop redirection
                Log('EdgeBrowser Loading Local Error Page');
                if NOT EdgeBrowser.NavigateToString(HtmlBody) then
                    Log('EdgeBrowser Navigate Failed Local Page');
            end;
        end;
    end;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.EdgeBrowserProcessFailed(Sender: TCustomEdgeBrowser; ProcessFailedKind: TOleEnum);
begin
   Log('EdgeBrowser Process Failed');
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// page URL changed
procedure TOAuthLoginFormE.EdgeBrowserSourceChanged(Sender: TCustomEdgeBrowser; IsNewDocument: Boolean);
begin
   NewURL := Sender.LocationURL;
   Log('EdgeBrowser Source URL Changed to: ' + NewURL);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.EdgeBrowserWebResourceRequested(Sender: TCustomEdgeBrowser; Args: TWebResourceRequestedEventArgs);
begin
//
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.EdgeBrowserWindowCloseRequested(Sender: TObject);
begin
   Log('EdgeBrowser Close Window');
   Close;
end;

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
// following events from olde TWebBrowser using Microsoft's Shell Doc Object and Control Library (SHDOCVW.DLL), part of Internet Explorer
// note events called are not consistent, so look for redirect URL in lots of them.
procedure TOAuthLoginFormE.HandleWBRedirect;
var
    aStream: TMemoryStream;
    CanCancel: Boolean;
    HtmlBody: String;
    ABody: AnsiString;
begin
    if Assigned(FonRedirEvent) then begin
        CanCancel := False;
        HtmlBody := '';
        FonRedirEvent(NewURL, HtmlBody, CanCancel);
        if CanCancel then begin
            Log('WebBrowser Authenication Completed Successfully');
            FRedirDone := True;
            ModalResult := mrOk;
            WebBrowser.Stop;  // try and stop redirection
            Self.Close;
        end
        else begin
            if HtmlBody <> '' then begin
                FRedirDone := True;
                WebBrowser.Stop;  // try and stop redirection
                Log('WebBrowser Loading Local Error Page');
                aStream := TMemoryStream.Create;
                try
                    Try
                        ABody := AnsiString(HtmlBody);
                        aStream.WriteBuffer (Pointer (ABody)^, Length (ABody));
                        aStream.Position := 0;
                        (WebBrowser.Document as IPersistStreamInit).Load (TStreamAdapter.Create(aStream)) ;
                    except
                        Log('WebBrowser Failed to Load Error Page');
                    End;
                finally
                    aStream.Free;
                end;
            end;
        end;
    end;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.WebBrowserBeforeNavigate2(ASender: TObject; const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);
begin
    if FRedirDone then Exit;
    TimerClose.Enabled := False;
    NewURL := URL;
    Log('WebBrowser Navigate Starting to: ' + NewURL);
    HandleWBRedirect;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.WebBrowserNavigateComplete2(ASender: TObject; const pDisp: IDispatch; const URL: OleVariant);
begin
    if FRedirDone then Exit;
    TimerClose.Enabled := False;
    Log('WebBrowser Navigate OK to ' + URL);
    NewURL := URL;
    HandleWBRedirect;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.WebBrowserNavigateError(ASender: TObject; const pDisp: IDispatch; const URL, Frame, StatusCode: OleVariant; var Cancel: WordBool);
begin
    if FRedirDone then Exit;
    TimerClose.Enabled := False;
    Log('WebBrowser Navigate Error to ' + URL);
    NewURL := URL;
    HandleWBRedirect;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.WebBrowserShowScriptError(ASender: TObject; const AErrorLine, AErrorCharacter, AErrorMessage, AErrorCode,
  AErrorUrl: OleVariant; var AOut: OleVariant; var AHandled: Boolean);
begin
   Log('WebBrowser Script Error to ' + AErrorURL + ' - ' + AErrorMessage);
   AHandled := True;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TOAuthLoginFormE.WebBrowserTitleChange(ASender: TObject; const Text: WideString);
begin
    NewTitle := Text;
    LabelTitle.Caption := NewTitle;
    Log('WebBrowser Title: ' + NewTitle);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
destructor TOAuthBrowser.Destroy;                                                     { V8.71 }
begin
    if OALoginFormShowing and Assigned(OAuthLoginFormE) then begin
        OAuthLoginFormE.Close;
        OAuthLoginFormE := Nil;
        OALoginFormShowing := False;
    end;
    inherited Destroy;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
function TOAuthBrowser.BrowserCheckEmbedded: Boolean;                                  { V8.71 }
{$IFNDEF FMX}
var
    ErrInfo: String;
{$ENDIF}
begin
{$IFNDEF FMX}
    Result := IcsCheckEdgeBrowser(ErrInfo);
{$ELSE}
    Result := False;
{$ENDIF}
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Display browser window with URL, supports Edge and MSIE engines, if available, and
  if supported by the authenication server, Google seems to dislike MSIE.
  Currently Delphi 10.4 and later only which support Edge, but older compilers planned.
  Edge should be available on Windows 10 and 11 provided updates have not been skipped. }

function TOAuthBrowser.BrowserEmbedded(RestOAuth: TRestOAuth; Owner: TComponent; const URL: string): Boolean;    { V8.71 }
var
    IgnoreEdge: Boolean;
    ErrInfo, MyTitle: String;
begin
    Result := False;
    ErrInfo := '';
    if NOT IcsCheckEdgeBrowser(ErrInfo) then begin
        RestOAuth.LogEvent('Embedded Edge Browser Error - ' + ErrInfo);
        IgnoreEdge := True;
    end
    else
        IgnoreEdge := False;
    MyTitle := RestOAuth.AccName;
    if MyTitle = '' then
        MyTitle := 'Please Login';

 // create modal OAuth Login Form, wait for user to login to cloud
    OAuthLoginFormE := TOAuthLoginFormE.Create(Owner);
    OAuthLoginFormE.onLogEvent := RestOAuth.FormLogEvent;
    OAuthLoginFormE.onRedirEvent := RestOAuth.FormRedirEvent;
    if NOT OAuthLoginFormE.GotoURL(URL, MyTitle, RestOAuth.LoginHint, RestOAuth.EdgeCacheDir, IgnoreEdge) then begin
        if OALoginFormShowing then begin
            OAuthLoginFormE.Close;
            OAuthLoginFormE := Nil;
            OALoginFormShowing := False;
        end;
    end
    else begin
        if (OAuthLoginFormE.ShowModal = mrOk) or (RestOAuth.AccToken <> '') then
            Result := True
        else
            RestOAuth.SetError(OAuthErrCancelled, 'OAuth Login Window Closed');
    end;
end;

{$ENDIF USE_SSL}


end.
