diff --git a/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/SimpleOSRBrowser.lpi b/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/SimpleOSRBrowser.lpi index 84ba6405..1778801f 100644 --- a/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/SimpleOSRBrowser.lpi +++ b/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/SimpleOSRBrowser.lpi @@ -14,7 +14,7 @@ - + @@ -59,7 +59,7 @@ - + diff --git a/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/umainform.pas b/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/umainform.pas index 5d163eeb..d9bfbc52 100644 --- a/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/umainform.pas +++ b/demos/Lazarus_Linux_QT5/SimpleOSRBrowser/umainform.pas @@ -119,6 +119,77 @@ implementation {$R *.lfm} +// This is a simple CEF browser in "off-screen rendering" mode (a.k.a OSR mode) + +// It uses the default CEF configuration with a multithreaded message loop and +// that means that the TChromium events are executed in a CEF thread. + +// QT is not thread safe so we have to save all the information given in the +// TChromium events and use it later in the main application thread. We use +// critical sections to protect all that information. + +// For example, the browser updates the mouse cursor in the +// TChromium.OnCursorChange event so we have to save the cursor in FPanelCursor +// and use Application.QueueAsyncCall to update the Panel1.Cursor value in the +// main application thread. + +// The raw bitmap information given in the TChromium.OnPaint event also needs to +// be stored in a TCEFBitmapBitBuffer class instead of a simple TBitmap to avoid +// issues with QT. + +// Chromium renders the web contents asynchronously. It uses multiple processes +// and threads which makes it complicated to keep the correct browser size. + +// In one hand you have the main application thread where the form is resized by +// the user. On the other hand, Chromium renders the contents asynchronously +// with the last browser size available, which may have changed by the time +// Chromium renders the page. + +// For this reason we need to keep checking the real size and call +// TChromium.WasResized when we detect that Chromium has an incorrect size. + +// TChromium.WasResized triggers the TChromium.OnGetViewRect event to let CEF +// read the current browser size and then it triggers TChromium.OnPaint when the +// contents are finally rendered. + +// TChromium.WasResized --> (time passes) --> TChromium.OnGetViewRect --> (time passes) --> TChromium.OnPaint + +// You have to assume that the real browser size can change between those calls +// and events. + +// This demo uses a couple of fields called "FResizing" and "FPendingResize" to +// reduce the number of TChromium.WasResized calls. + +// FResizing is set to True before the TChromium.WasResized call and it's set to +// False at the end of the TChromium.OnPaint event. + +// FPendingResize is set to True when the browser changed its size while +// FResizing was True. The FPendingResize value is checked at the end of +// TChromium.OnPaint to check the browser size again because it changed while +// Chromium was rendering the page. + +// The TChromium.OnPaint event in the demo also calls +// TBufferPanel.UpdateOrigBufferDimensions and TBufferPanel.BufferIsResized to check +// the width and height of the buffer parameter, and the internal buffer size in +// the TBufferPanel component. + +// Lazarus usually initializes the QT WidgetSet in the initialization section +// of the "Interfaces" unit which is included in the LPR file. This causes +// initialization problems in CEF and we need to call "CreateWidgetset" after +// the GlobalCEFApp.StartMainProcess call. + +// Lazarus shows a warning if we remove the "Interfaces" unit from the LPR file +// so we created a custom unit with the same name that includes two procedures +// to initialize and finalize the WidgetSet at the right time. + +// This is the destruction sequence in OSR mode : +// 1- FormCloseQuery sets CanClose to the initial FCanClose value (False) and +// calls Chromium1.CloseBrowser(True) which will destroy the internal browser +// immediately. +// 2- Chromium1.OnBeforeClose is triggered because the internal browser was +// destroyed. FCanClose is set to True and calls +// SendCompMessage(CEF_BEFORECLOSE) to close the form asynchronously. + uses Math, uCEFMiscFunctions, uCEFApplication, uCEFConstants, uCEFBitmapBitBuffer, @@ -145,7 +216,8 @@ begin GlobalCEFApp.LogSeverity := LOGSEVERITY_INFO; GlobalCEFApp.RootCache := 'RootCache'; GlobalCEFApp.SetCurrentDir := True; - GlobalCEFApp.DisableZygote := True; + GlobalCEFApp.DisableZygote := True; + GlobalCEFApp.WindowlessRenderingEnabled := True; GlobalCEFApp.OnContextInitialized := @GlobalCEFApp_OnContextInitialized; end; diff --git a/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.lfm b/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.lfm index ed963bb5..6dd632e7 100644 --- a/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.lfm +++ b/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.lfm @@ -116,7 +116,6 @@ object MainForm: TMainForm OnBeforePopup = Chromium1BeforePopup OnAfterCreated = Chromium1AfterCreated OnBeforeClose = Chromium1BeforeClose - OnClose = Chromium1Close OnOpenUrlFromTab = Chromium1OpenUrlFromTab Left = 106 Top = 69 diff --git a/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.pas b/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.pas index d30ead86..0383f85f 100644 --- a/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.pas +++ b/demos/Lazarus_Linux_QT6/SimpleBrowser/umainform.pas @@ -26,7 +26,6 @@ type procedure Chromium1AfterCreated(Sender: TObject; const browser: ICefBrowser); procedure Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser); procedure Chromium1BeforePopup(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; popup_id: Integer; 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 Chromium1Close(Sender: TObject; const browser: ICefBrowser; var aAction: TCefCloseBrowserAction); procedure Chromium1GotFocus(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); @@ -69,6 +68,33 @@ implementation {$R *.lfm} +// This is a demo with the simplest web browser you can build using CEF4Delphi and +// it doesn't show any sign of progress like other web browsers do. + +// Remember that it may take a few seconds to load if Windows update, your antivirus or +// any other windows service is using your hard drive. + +// Depending on your internet connection it may take longer than expected. + +// Please check that your firewall or antivirus are not blocking this application +// or the domain "google.com". If you don't live in the US, you'll be redirected to +// another domain which will take a little time too. + +// This demo uses a TChromium and a TCEFLinkedWindowParent + +// We need to use TCEFLinkedWindowParent in Linux to update the browser +// visibility and size automatically. + +// Most of the TChromium events are executed in a CEF thread and this causes +// issues with most QT API functions. If you need to update the GUI, store the +// TChromium event parameters and use SendCompMessage (Application.QueueAsyncCall) +// to do it in the main application thread. + +// Destruction steps +// ================= +// 1. FormCloseQuery sets CanClose to FALSE, destroys CEFLinkedWindowParent1 and calls TChromium.CloseBrowser which triggers the TChromium.OnBeforeClose event. +// 2. TChromium.OnBeforeClose sets FCanClose := True and sends CEF_BEFORECLOSE to close the form. + uses Math, uCEFMiscFunctions, uCEFApplication, uCEFLinuxFunctions, uCEFConstants; @@ -145,9 +171,7 @@ begin FClosing := True; Visible := False; Chromium1.CloseBrowser(True); - - if Chromium1.RuntimeStyle = CEF_RUNTIME_STYLE_CHROME then - CEFLinkedWindowParent1.Free; + CEFLinkedWindowParent1.Free; end; end; {%Endregion} @@ -262,13 +286,6 @@ begin Result := (targetDisposition in [CEF_WOD_NEW_FOREGROUND_TAB, CEF_WOD_NEW_BACKGROUND_TAB, CEF_WOD_NEW_POPUP, CEF_WOD_NEW_WINDOW]); end; -procedure TMainForm.Chromium1Close(Sender: TObject; const browser: ICefBrowser; - var aAction: TCefCloseBrowserAction); -begin - // continue closing the browser - aAction := cbaClose; -end; - procedure TMainForm.Chromium1GotFocus(Sender: TObject; const browser: ICefBrowser); begin diff --git a/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/SimpleOSRBrowser.lpi b/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/SimpleOSRBrowser.lpi index b05b494f..9a76bdd6 100644 --- a/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/SimpleOSRBrowser.lpi +++ b/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/SimpleOSRBrowser.lpi @@ -80,9 +80,6 @@ - - - diff --git a/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/umainform.pas b/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/umainform.pas index 030f30d8..71bbe732 100644 --- a/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/umainform.pas +++ b/demos/Lazarus_Linux_QT6/SimpleOSRBrowser/umainform.pas @@ -119,6 +119,77 @@ implementation {$R *.lfm} +// This is a simple CEF browser in "off-screen rendering" mode (a.k.a OSR mode) + +// It uses the default CEF configuration with a multithreaded message loop and +// that means that the TChromium events are executed in a CEF thread. + +// QT is not thread safe so we have to save all the information given in the +// TChromium events and use it later in the main application thread. We use +// critical sections to protect all that information. + +// For example, the browser updates the mouse cursor in the +// TChromium.OnCursorChange event so we have to save the cursor in FPanelCursor +// and use Application.QueueAsyncCall to update the Panel1.Cursor value in the +// main application thread. + +// The raw bitmap information given in the TChromium.OnPaint event also needs to +// be stored in a TCEFBitmapBitBuffer class instead of a simple TBitmap to avoid +// issues with QT. + +// Chromium renders the web contents asynchronously. It uses multiple processes +// and threads which makes it complicated to keep the correct browser size. + +// In one hand you have the main application thread where the form is resized by +// the user. On the other hand, Chromium renders the contents asynchronously +// with the last browser size available, which may have changed by the time +// Chromium renders the page. + +// For this reason we need to keep checking the real size and call +// TChromium.WasResized when we detect that Chromium has an incorrect size. + +// TChromium.WasResized triggers the TChromium.OnGetViewRect event to let CEF +// read the current browser size and then it triggers TChromium.OnPaint when the +// contents are finally rendered. + +// TChromium.WasResized --> (time passes) --> TChromium.OnGetViewRect --> (time passes) --> TChromium.OnPaint + +// You have to assume that the real browser size can change between those calls +// and events. + +// This demo uses a couple of fields called "FResizing" and "FPendingResize" to +// reduce the number of TChromium.WasResized calls. + +// FResizing is set to True before the TChromium.WasResized call and it's set to +// False at the end of the TChromium.OnPaint event. + +// FPendingResize is set to True when the browser changed its size while +// FResizing was True. The FPendingResize value is checked at the end of +// TChromium.OnPaint to check the browser size again because it changed while +// Chromium was rendering the page. + +// The TChromium.OnPaint event in the demo also calls +// TBufferPanel.UpdateOrigBufferDimensions and TBufferPanel.BufferIsResized to check +// the width and height of the buffer parameter, and the internal buffer size in +// the TBufferPanel component. + +// Lazarus usually initializes the QT WidgetSet in the initialization section +// of the "Interfaces" unit which is included in the LPR file. This causes +// initialization problems in CEF and we need to call "CreateWidgetset" after +// the GlobalCEFApp.StartMainProcess call. + +// Lazarus shows a warning if we remove the "Interfaces" unit from the LPR file +// so we created a custom unit with the same name that includes two procedures +// to initialize and finalize the WidgetSet at the right time. + +// This is the destruction sequence in OSR mode : +// 1- FormCloseQuery sets CanClose to the initial FCanClose value (False) and +// calls Chromium1.CloseBrowser(True) which will destroy the internal browser +// immediately. +// 2- Chromium1.OnBeforeClose is triggered because the internal browser was +// destroyed. FCanClose is set to True and calls +// SendCompMessage(CEF_BEFORECLOSE) to close the form asynchronously. + uses Math, uCEFMiscFunctions, uCEFApplication, uCEFConstants, uCEFBitmapBitBuffer, @@ -143,6 +214,7 @@ begin GlobalCEFApp := TCefApplication.Create; GlobalCEFApp.LogFile := 'debug.log'; GlobalCEFApp.LogSeverity := LOGSEVERITY_INFO; + GlobalCEFApp.WindowlessRenderingEnabled := True; GlobalCEFApp.RootCache := 'RootCache'; GlobalCEFApp.SetCurrentDir := True; GlobalCEFApp.DisableZygote := True; diff --git a/update_CEF4Delphi.json b/update_CEF4Delphi.json index c7b1e6e8..d34dfc36 100644 --- a/update_CEF4Delphi.json +++ b/update_CEF4Delphi.json @@ -2,7 +2,7 @@ "UpdateLazPackages" : [ { "ForceNotify" : true, - "InternalVersion" : 790, + "InternalVersion" : 791, "Name" : "cef4delphi_lazarus.lpk", "Version" : "140.1.14" }