I want to build the JS Extension demo project included with CEF4Delphi downloaded from https://github.com/salvadordf/CEF4Delphi, which has been installed on C++Builder XE7. My goal is to send messages and variables from Chromium (webpage, javascript) to native C++ function.
I found the delphi demo which works good. But I need to translate to C++Builder, and I translated almost of code. It works good if I do not set OnWebKitInitialized member of TCefApplication. But I need to set in order to get my extension registered, so when I do this, application compile and builds good but browser has white background and nothing shows. I need to get this demo working in C++Builder. I have attached source code bellow.
Unit1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include "uCEFChromium.hpp"
#include "uCEFWinControl.hpp"
#include "uCEFWindowParent.hpp"
#include <Vcl.ComCtrls.hpp>
#include <Vcl.ExtCtrls.hpp>
#include "uTestExtensionHandler.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TPanel *NavControlPnl;
TEdit *Edit1;
TButton *GoBtn;
TStatusBar *StatusBar1;
TCEFWindowParent *CEFWindowParent1;
TChromium *Chromium1;
TTimer *Timer1;
void __fastcall Chromium1AfterCreated(TObject *Sender, ICefBrowser * const browser);
void __fastcall Chromium1BeforeClose(TObject *Sender, ICefBrowser * const browser);
void __fastcall Chromium1BeforeContextMenu(TObject *Sender, ICefBrowser * const browser,
ICefFrame * const frame, ICefContextMenuParams * const params,
ICefMenuModel * const model);
void __fastcall Chromium1BeforePopup(TObject *Sender, ICefBrowser * const browser,
ICefFrame * const frame, const ustring targetUrl, const ustring targetFrameName,
TCefWindowOpenDisposition targetDisposition, bool userGesture,
const TCefPopupFeatures &popupFeatures, TCefWindowInfo &windowInfo,
ICefClient *&client, TCefBrowserSettings &settings, bool &noJavascriptAccess,
bool &Result);
void __fastcall Chromium1Close(TObject *Sender, ICefBrowser * const browser, bool Result);
void __fastcall Chromium1ContextMenuCommand(TObject *Sender, ICefBrowser * const browser,
ICefFrame * const frame, ICefContextMenuParams * const params,
int commandId, DWORD eventFlags, bool Result);
void __fastcall Chromium1ProcessMessageReceived(TObject *Sender, ICefBrowser * const browser,
TCefProcessId sourceProcess, ICefProcessMessage * const message,
bool Result);
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall GoBtnClick(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormShow(TObject *Sender);
void __fastcall FormDestroy(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
protected:
// Variables to control when can we destroy the form safely
bool FCanClose; // Set to True in TChromium.OnBeforeClose
bool FClosing; // Set to True in the CloseQuery event.
void __fastcall BrowserCreatedMsg(TMessage &Message);
void __fastcall BrowserDestroyMsg(TMessage &Message);
void __fastcall WMMove(TMessage &Message);
void __fastcall WMMoving(TMessage &Message);
void __fastcall WMEnterMenuLoop(TMessage &Message);
void __fastcall WMExitMenuLoop(TMessage &Message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CEF_AFTERCREATED, TMessage, BrowserCreatedMsg)
MESSAGE_HANDLER(CEF_DESTROY, TMessage, BrowserDestroyMsg)
MESSAGE_HANDLER(WM_MOVE, TMessage, WMMove)
MESSAGE_HANDLER(WM_MOVING, TMessage, WMMoving)
MESSAGE_HANDLER(WM_ENTERMENULOOP, TMessage, WMEnterMenuLoop)
MESSAGE_HANDLER(WM_EXITMENULOOP, TMessage, WMExitMenuLoop)
END_MESSAGE_MAP(TForm)
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "uCEFChromium"
#pragma link "uCEFWinControl"
#pragma link "uCEFWindowParent"
#pragma resource "*.dfm"
TForm1 *Form1;
void GlobalCEFApp_OnWebKitInitialized()
{
String TempExtensionCode;
//ICefv8Handler *TempHandler;
_di_ICefv8Handler TempHandler;
// This is a JS extension example with 2 functions and several parameters.
// Please, read the "JavaScript Integration" wiki page at
// https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md
TempExtensionCode = "var myextension;\
if (!myextension)\
myextension = {};\
(function() {\
myextension.mouseover = function(a) {\
native function mouseover();\
mouseover(a);\
};\
myextension.sendresulttobrowser = function(b,c) {\
native function sendresulttobrowser();\
sendresulttobrowser(b,c);\
};\
})();";
try {
TempHandler = TTestExtensionHandler();
CefRegisterExtension("myextension", TempExtensionCode, TempHandler);
}
__finally {
TempHandler = NULL;
}
}
class TWebKitInitRef : public TCppInterfacedObject<TOnWebKitInitializedEvent>
{
public:
//TWebKitInitRef(){ Invoke();}
INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
void __fastcall Invoke() { GlobalCEFApp_OnWebKitInitialized(); }
};
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
GlobalCEFApp = new TCefApplication();
GlobalCEFApp->CheckCEFFiles =true;
GlobalCEFApp->OnWebKitInitialized = _di_TOnWebKitInitializedEvent(new TWebKitInitRef());
GlobalCEFApp->LogFile = "debug.log";
GlobalCEFApp->LogSeverity = LOGSEVERITY_INFO;
if(! GlobalCEFApp->StartMainProcess())Form1->Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1AfterCreated(TObject *Sender, ICefBrowser * const browser)
{
PostMessage(Handle, CEF_AFTERCREATED, 0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1BeforeClose(TObject *Sender, ICefBrowser * const browser)
{
FCanClose = true;
PostMessage(Handle, WM_CLOSE, 0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1BeforeContextMenu(TObject *Sender, ICefBrowser * const browser,
ICefFrame * const frame, ICefContextMenuParams * const params,
ICefMenuModel * const model)
{
// Adding some custom context menu entries
model->AddSeparator();
model->AddItem(MENU_ID_USER_FIRST + 1, "Set mouseover event");
model->AddItem(MENU_ID_USER_FIRST + 2, "Visit DOM in JavaScript");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1BeforePopup(TObject *Sender, ICefBrowser * const browser,
ICefFrame * const frame, const ustring targetUrl, const ustring targetFrameName,
TCefWindowOpenDisposition targetDisposition, bool userGesture,
const TCefPopupFeatures &popupFeatures, TCefWindowInfo &windowInfo,
ICefClient *&client, TCefBrowserSettings &settings, bool &noJavascriptAccess,
bool &Result)
{
Result = targetDisposition==WOD_NEW_FOREGROUND_TAB || targetDisposition==WOD_NEW_BACKGROUND_TAB || targetDisposition==WOD_NEW_POPUP || targetDisposition==WOD_NEW_WINDOW ? true: false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1Close(TObject *Sender, ICefBrowser * const browser,
bool Result)
{
PostMessage(Handle, CEF_DESTROY, 0, 0);
Result = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1ContextMenuCommand(TObject *Sender, ICefBrowser * const browser,
ICefFrame * const frame, ICefContextMenuParams * const params,
int commandId, DWORD eventFlags, bool Result)
{
Result = false;
// Here is the code executed for each custom context menu entry
switch( commandId)
{
case (MENU_ID_USER_FIRST + 1) :
if ((browser != NULL) && (browser->MainFrame != NULL))
browser->MainFrame->ExecuteJavaScript("document.body.addEventListener('mouseover', function(evt){\
function getpath(n){\
var ret = '<' + n.nodeName + '>';\
if (n.parentNode){return getpath(n.parentNode) + ret} else \
return ret\
};\
myextension.mouseover(getpath(evt.target))})",
// This is the call from JavaScript to the extension with DELPHI code in uTestExtensionHandler.pas
"about:blank", 0);
case (MENU_ID_USER_FIRST + 2) :
if ((browser != NULL) && (browser->MainFrame != NULL))
browser->MainFrame->ExecuteJavaScript("var testhtml = document.body.innerHTML;\
myextension.sendresulttobrowser(testhtml, " + QuotedStr((AnsiString)"customname") + ");", // This is the call from JavaScript to the extension with DELPHI code in uTestExtensionHandler.pas
"about:blank", 0);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Chromium1ProcessMessageReceived(TObject *Sender, ICefBrowser * const browser,
TCefProcessId sourceProcess, ICefProcessMessage * const message,
bool Result)
{
if ((message == NULL) || (message->ArgumentList == NULL)) exit;
// This function receives the messages with the JavaScript results
// Many of these events are received in different threads and the VCL
// doesn't like to create and destroy components in different threads.
// It's safer to store the results and send a message to the main thread to show them.
// The message names are defined in the extension or in JS code.
if (message->Name == "mouseover")
{
StatusBar1->Panels->Items[0]->Text = message->ArgumentList->GetString(0);
Result = true;
}
else
if (message->Name == "customname")
{
StatusBar1->Panels->Items[0]->Text = message->ArgumentList->GetString(0);
Result = true;
}
else Result = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
Timer1->Enabled = false;
if(! (Chromium1->CreateBrowser(CEFWindowParent1, "")) && !(Chromium1->Initialized))
Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::GoBtnClick(TObject *Sender)
{
Chromium1->LoadURL(Edit1->Text);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
//Action = FCanClose ? caFree : caNone;
if (!FClosing)
{
FClosing = true;
Visible = false;
Chromium1->CloseBrowser(true);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
FCanClose = false;
FClosing = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
StatusBar1->Panels->Items[0]->Text= "Initializing browser. Please wait...";
// 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 (!Chromium1->CreateBrowser(CEFWindowParent1, "")) Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
GlobalCEFApp->~TCefApplication();
DestroyGlobalCEFApp();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMMove(TMessage &Message)
{
if (Chromium1 != NULL) Chromium1->NotifyMoveOrResizeStarted();
}
void __fastcall TForm1::WMMoving(TMessage &Message)
{
if (Chromium1 != NULL) Chromium1->NotifyMoveOrResizeStarted();
}
void __fastcall TForm1::WMEnterMenuLoop(TMessage &Message)
{
if ((Message.WParam == 0) && (GlobalCEFApp != NULL)) GlobalCEFApp->OsmodalLoop = true;
}
void __fastcall TForm1::WMExitMenuLoop(TMessage &Message)
{
if ((Message.WParam == 0) && (GlobalCEFApp != NULL)) GlobalCEFApp->OsmodalLoop = false;
}
void __fastcall TForm1::BrowserCreatedMsg(TMessage &Message)
{
StatusBar1->Panels->Items[0]->Text = "";
CEFWindowParent1->UpdateSize();
NavControlPnl->Enabled = true;
GoBtn->Click();
}
void __fastcall TForm1::BrowserDestroyMsg(TMessage &Message)
{
CEFWindowParent1->Free();
}
uTestExtensionHandler.h
// ************************************************************************
// ***************************** CEF4Delphi *******************************
// ************************************************************************
//
// CEF4Delphi is based on DCEF3 which uses CEF3 to embed a chromium-based
// browser in Delphi applications.
//
// The original license of DCEF3 still applies to CEF4Delphi.
//
// For more information about CEF4Delphi visit :
// https://www.briskbard.com/index.php?lang=en&pageid=cef
//
#ifndef uTestExtensionHandlerH
#define uTestExtensionHandlerH
#include "uCEFRenderProcessHandler.hpp"
#include "uCEFBrowserProcessHandler.hpp"
#include "uCEFInterfaces.hpp"
#include "uCEFProcessMessage.hpp"
#include "uCEFv8Context.hpp"
#include "uCEFTypes.hpp"
#include "uCEFv8Handler.hpp";
class TTestExtensionHandler : public _di_ICefv8Handler//public ICefv8Handler
{
protected:
//bool __fastcall Execute(ustring name, ICefv8Value *obj, TCefv8ValueArray arguments, ICefv8Value *retval, ustring exception);
bool __fastcall Execute(const Uceftypes::ustring name, const _di_ICefv8Value obj, const TCefv8ValueArray arguments, _di_ICefv8Value &retval, Uceftypes::ustring &exception);
};
//uses uCEFMiscFunctions, uCEFConstants, uJSExtension;
bool __fastcall TTestExtensionHandler::Execute(const Uceftypes::ustring name, const _di_ICefv8Value obj, const TCefv8ValueArray arguments, _di_ICefv8Value &retval, Uceftypes::ustring &exception)
{
ICefProcessMessage *msg;
bool Result;
if (name == "mouseover")
{
if ((arguments.Length > 0) && arguments[0]->IsString())
{
msg = TCefProcessMessageRef::New("mouseover");
msg->ArgumentList->SetString(0, arguments[0]->GetStringValue());
TCefv8ContextRef::Current()->Browser->SendProcessMessage(PID_BROWSER, msg);
}
Result = true;
}
else
if (name == "sendresulttobrowser")
{
if ((arguments.Length > 1) && arguments[0]->IsString() && arguments[1]->IsString())
{
msg = TCefProcessMessageRef::New(arguments[1]->GetStringValue());
msg->ArgumentList->SetString(0, arguments[0]->GetStringValue());
TCefv8ContextRef::Current()->Browser->SendProcessMessage(PID_BROWSER, msg);
}
Result = true;
}
else
Result = false;
return Result;
}
#endif
I expect to register correctly my extension so I could be able to return messages and results variables from webpage to native c++ function. I did almost the same from delphi demo, but cannot have it working in c++builder. What should I change to get this working ?
UPDATE:
I have tried a second method:
#include "uCEFv8Value.hpp"
class MyV8Handler : public _di_ICefv8Handler {
public:
MyV8Handler() {} ;
virtual bool __fastcall Execute(const Uceftypes::ustring name, const _di_ICefv8Value obj, const TCefv8ValueArray arguments, _di_ICefv8Value &retval, Uceftypes::ustring &exception)
{
if (name == "myfunc") {
// Extract argument values
// Return my string value.
retval = TCefv8ValueRef::NewString("My Value!");
return true;
}
// Function does not exist.
return false;
}
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(MyV8Handler);
};
class TContextRef : public TCppInterfacedObject<TOnContextCreatedEvent>
{
public:
//TContextRef(){ Invoke();}
INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
void __fastcall Invoke(const _di_ICefBrowser browser, const _di_ICefFrame frame, const _di_ICefv8Context context)
{
// Retrieve the context's window object.
_di_ICefv8Value object = context->GetGlobal();
// Create an instance of my CefV8Handler object.
ICefv8Handler *handler = dynamic_cast<ICefv8Handler*>(new MyV8Handler());
// Create the "myfunc" function.
_di_ICefv8Value func = TCefv8ValueRef::NewFunction("myfunc", handler);
// Add the "myfunc" function to the "window" object.
object->SetValueByKey("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
};
...
GlobalCEFApp->OnContextCreated = _di_TOnContextCreatedEvent(new TContextRef());
Chromium1->ExecuteJavaScript("alert(window.myfunc('someString'));", "", 0);
I think that GlobalCEFApp->OnContextCreated
is not Ivoked, samething like GlobalCEFApp->OnWebKitInitialized
. Maybe the reference to function is not passed correctly ?