Delphi работа с javascript

Блог о быдло разработке программного обеспечения

Блог о разработке программного обеспечения на java, Delphi, С# и баз данных.

пятница, 22 января 2016 г.

Delphi TWebBrowser выполнение JavaScript

в посте описано как в компоненте TWebBrowser выполнить скрипт JavaScript. Для выполнения скрипта потребуются подключить модули MSHTML_TLB, SHDocVw. Модуль SHDocVw идет в комплексе Delphi, а вот модуль MSHTML_TLB нужно экспортировать с Microsoft HTML Object Library. Модуль MSHTML_TLB нужен для определения интерфейса IHTMLDocument2 и выполнения скриптов.
Для получения файла модуля выполним следующие действия:

  • В Delphi открываем Component -> Import ActiveX Control.
  • В появившемся окне выбираем Import ActiveX Control, нажимаем Next>>
  • В списке выбираем «Microsoft HTML Object Library», нажимаем Next>>
  • Указываем каталог куда положим модуль
  • Далее на своё усмотрение выбираем создать модуль, добавить его в проект или установить и нажимаем Finish.

К модулю где будет реализовываться функция подключаем в uses SHTML_TLB, SHDocVw.

procedure ExecuteScript(doc: IHTMLDocument2; script: string; language: string); begin if doc <> nil then begin if doc.parentWindow <> nil then doc.parentWindow.ExecScript(script, Olevariant(language)) ; end; end; 

Пример использования данной процедуры.

var script : string; begin //Данный скрипт найдет первый элемент с ID равной = "main" и покажет его тэги; script := 'var elemMain = document.getElementById("main"); '+ 'if (elemMain != null) < alert(elemMain.tagName) ; >'; ExecuteScript(EmbeddedWB1.Document as IHTMLDocument2, script, 'javascript'); end; 

Источник

Getting started with SpiderMonkey to run JavaScript in Delphi

Run JavaScript in Delphi

Customer Portal

The scripting is necessary to customize the building process. PascalScript provides wide opportunities. However, there is the other scripting language, JavaScript, which can be considered as a more powerful and flexible tool for implementing specific building and deployment tasks. There is a huge number of modules, extensions, and frameworks, which allow you to solve a wide range of tasks with the ability to scale solutions

Читайте также:  Get page html online

Therefore, the next step in the TaskRunner development was to add the JavaScript support. Before we get started searching for a suitable engine, let’s list the requirements:

So, to make things working, I had to solve the following tasks:

  1. The possibility to transfer data from a script to Delphi, and back;
  2. Working with the file system, writing, and reading text files. The ActiveX technology and it’s FileSystemObject are deprecated, so fs Node.js is preferable;
  3. Running scripts in threads.

Nowadays, there are lots of scripting engines, including ones with open-source code. You can read an overview of different scripting engines in the following article: Delphi JavaScript execution — ditch TWebBrowser for ChakraCore

This is possible the best overview I’ve ever read about JavaScript integrations in Delphi. The V8 engine is rather powerful. However, has some problems integrating with Delphi. Besen represents 100% Delphi implementation. But it also has known performance issues. The Microsoft ChakraCore library and an open-sourced Delphi/FPC binding look very promising. But in my quick attempts, I couldn’t use Node.js modules in scripts.

Finally, the choice was made in favor of SpiderMonkey. This very powerful engine is a Mozilla product, and is included in the FireFox web browser.

Thus, we have the following tasks:

  1. Installing SpiderMonkey;
  2. Writing a test Delphi app, which runs a test JavaScript code;
  3. Testing the engine in threads.

Installing SpiderMonkey

The SpiderMonkey engine has a very good integration with Delphi, which is implemented in the Synopse mORMot project.

A brief study of demos and tests, which are available in their GitHub repository, gives us good usage examples. You can declare classes in Delphi, create, and use instances of these classes in a script, as well as transfer data from Delphi to a script, and back using class properties and methods. It looks like this is what we need. In addition, this scripting engine allows you to debug scripts. But let’s keep the script debugging for the future article.

SpiderMonkey, as every open-source product, requires some work for installing and using. In addition, mORMot requires a patched version of SpiderMonkey. The patches concern external procedure declarations: mORMot uses extern «C» for all imported DLL functions.

You can learn more in the mORMot installation instructions, which are available in their GitHub repository: SpiderMonkey build instructions

There are two ways to get started. You can download the SpiderMonkey sources, patch them, and compile on your side. You must be experienced in Mozilla Build and have the time to set up numerous configurations. The other way is to download precompiled SpiderMonkey DLLs using the links from the Synopse website. I used SpiderMonkey 52, as recommended by Synopse: SpiderMonkey downloads

However, there is one issue: the website, which contains the DLL archives, is not available from everywhere. If you are in a “wrong” location, like me, you will unable to download DLLs. The solution is simple: don’t doubt to specify some proxy in your web browser.

Next, you need to download the SyNode sources — a Delphi integration library for SpiderMonkey. SyNode is a part of the mORMot project, and is available on GitHub, see the mORMot repository

At the time of writing this article, the master branch experienced difficulties when compiling the project. Therefore, let’s use the sources from the latest stable release instead.

Writing and running a test app

Among the demos available within the mORMot repo, there are ones, which do everything we need. Let’s take a look at the SpiderMonkey45Binding.dproj project.

This project registers global objects, transfers data from Delphi using object properties, calls Delphi functions from a script, and loads Node.js modules. In particular, the example shows how to use the «fs», «path», and «http» modules. There is also an example of a custom module, which is loaded from DLL. The sources for this DLL module are available in the repository, as well. But in my first attempt, this module didn’t not work. I decided not to dig out the problem and focused on the listed above tasks, required to incorporate the engine into TaskRunner.

Let’s create a new Delphi project and add all necessary parts from the mentioned demo. You must specify paths to the moRMot source folders. There are two important classes, TSMEngineManager and TSMEngine, which are necessary to work with this scripting engine.

Next, create a new Delphi class, and make it available from a script. The class methods, which are necessary in a script, must be declared within the «published» section. This allows the mORMot runtime to automatically register these methods in the scripting engine. The created Delphi class must be enclosed in the compiler option. As an alternative, you can inherit your class from TPersistent.

In order to use the same class instance within different threads, we need to protect the FData field using a critical section — paramAccessor: TCriticalSection. This allows us to avoid a thread race condition in the future.

paramAccessor.Enter();
try
vp.rval := SimpleVariantToJSval(cx, FData.Values[vp.argv[0].asJSString.ToString(cx)]);
finally
paramAccessor.Leave();
end;

if not (vp.argv[0].isString and vp.argv[1].isString) then
begin
raise ESMException.Create(‘setValue parameters should be strings’);
end;

paramAccessor.Enter(); //allows to avoid the thread racing condition
try
FData.Values[vp.argv[0].asJSString.ToString(cx)] := vp.argv[1].asJSString.ToString(cx);
finally
paramAccessor.Leave();
end;

The registration of the class as a global object occurs in the TSMEngineManager.OnNewEngine event handler. The created TSMEngine instance requires a name. So, a script debugger will have the ability to connect to the engine for tracing.

// [Delphi]
procedure TForm1.DoOnCreateNewEngine(const aEngine: TSMEngine);
begin
aEngine.defineClass(FJobParams.ClassType, TSMSimpleRTTIProtoObject, aEngine.GlobalObject);
aEngine.GlobalObject.ptr.DefineProperty(aEngine.cx, ‘jobParams’,
CreateJSInstanceObjForSimpleRTTI(aEngine.cx, FJobParams, aEngine.GlobalObject),
JSPROP_ENUMERATE or JSPROP_READONLY or JSPROP_PERMANENT
);
end;
// [Delphi]
function TForm1.DoOnGetEngineName(const aEngine: TSMEngine): RawUTF8;
begin
Result := ‘FormEngine’;
end;

In the OnFormCreate event when the application starts, we create a manager. The engine is accessed through the manager, namely through FSMManager.ThreadSafeEngine().

FSMManager := TSMEngineManager.Create(»); //because we use Node.js modules from a .res file.
FSMManager.MaxPerEngineMemory := 512 * 1024 * 1024;
FSMManager.OnNewEngine := DoOnCreateNewEngine;
FSMManager.OnGetName := DoOnGetEngineName;

// [Delphi]
procedure TForm1.FormDestroy(Sender: TObject);
begin
FSMManager.ReleaseCurrentThreadEngine();
FSMManager.Free();
FJobParams.Free();
end;

A test JavaScript will look as follows:

We can add BOM (\ ufeff) at the beginning of the text to indicate that the file uses Unicode, as well as to indirectly indicate the encoding.

The script runs using the TSMEngine.Evaluate method. You must specify a name for the script. We will show the process of data transferring between Delphi and JavaScript using the registered global object. The script reads data from the global object, modifies, and returns back to the object. Additionally, the script saves data to a file using the «fs» module from Node.js.

// [Delphi]
procedure TForm1.btnEvaluateClick(Sender: TObject);
var
res: jsval;
begin
FEngine.Evaluate(memSource.Lines.Text, ‘script.js’, 1, res);
ShowMessage(‘Done: ‘ + FJobParams.Data.Text);//modified data are shown here
end;

The script works like a charm. It’s time to test the code in threads. To do this, let’s create the TScriptThread class — a TThread descendant.

The SyNode integration assumes one single TSMEngineManager and multiple TSMEngine instances. Due to the TaskRunner’s implementation specific, we cannot share the same instance of TSMEngineManager between running jobs. Therefore, we have to create a new instance of TSMEngineManager along with TSMEngine per each thread. Theoretically, this may cause performance issues. However, this decision allows us to quickly integrate SpiderMonkey into TaskRunner with minimum coding. So, the test code will look as follows:

for i := 0 to count — 1 do
begin
//this simple trick requires the hello.txt name in the javaScript source and allows each thread to use its own filename
thread := TScriptThread.Create(StringReplace(script, ‘hello.txt’, ‘hello’ + IntToStr(i) + ‘.txt’, [rfIgnoreCase, rfReplaceAll]), FJobParams);
thread.FreeOnTerminate := True;
thread.Start();
end;

Conclusion

You can download the source code for all the examples in this article on GitHub

SpiderMonkey is a great engine, which can do all the tasks we need. Now, we are actively working on adding SpiderMonkey to the TaskRunner app. I think, when this article will be published, we will release a new version of this build automation tool, as well.

Follow us in our Facebook group, Twitter and Telegram channel.

Feel free to subscribe to our Email list

Источник

Оцените статью