Considerations on 1C:Enterprise web client


If you wish to leave comments or ask questions, please log on or sign up.
One of the nice things about 1C:Enterprise is that you can run an application developed using the "managed forms" technology not only in the thin client for Windows, Linux, or MacOS X, but also in the web client that supports 5 browsers, including Chrome, Internet Explorer, Firefox, Safari, and Edge. And you don’t even need to change anything in the application source code!

Moreover, the application has almost the same look and feel in both clients. Try and see if you can find a few differences:

Thin client window on Linux:

f9066deee7e0470c8f0894412e195899.png
Click the picture to enlarge it

The same window in the web client (Google Chrome):

8989e9b05780acc3b5e7ba31a25e848b.png
Click the picture to enlarge it

Why a web client? In short, it is a feature that the spirit of times demands of us. It's been a long while since Internet connectivity became a standard requirement for business applications. To start with, we added the capacity to work over the Internet to our 1C:Enterprise thin client. Some of our competitors did the same and stopped at that point, others decided to abandon the thin client in favor of the web client, while we decided to give our customers a choice between these two options.

5d85b9c4ea9ca4a7dad38c74243904cc.png

Adding Internet connectivity to the thin client turned out to be a large project that led us to change the entire client/server architecture. And the web client was a completely new thing that we developed from scratch.

Setting the goal

In accordance with the project requirements, the web client must be capable of doing all the things that the thin client can do, namely:

  1. Display the user interface
  2. Run the client code written in 1C:Enterprise script

1C:Enterprise developers create the user interface in a visual editor in declarative style (without specifying the exact positions of the controls). They can choose from about thirty control types: buttons, several kinds of text boxes (for entering text, numbers, or date and time), lists, tables, charts, and other items.

The 1C:Enterprise client code can include server calls, operations with local resources (such as files), print jobs, and more.

Both the thin client that works over the Internet and the web client use a single set of web services for communicating with the 1C:Enterprise application server. Of course the implementations differ: the thin client is written in C++, while the web client is written in JavaScript.

Background story

The web client development project started in 2006, with a team of (more or less) 5 people. Some project stages required the engagement of developers with expertise in specific functionalities, such as spreadsheet documents or charts. They were mostly the same people who initially developed these functionalities for the thin client. This time they wrote the similar algorithms in JavaScript rather than C++.

At the very beginning we dismissed the idea of automatic or semi-automatic conversion of the C++ code that was used in the thin client into JavaScript because of conceptual differences between these two languages. Instead, we wrote the web client in JavaScript from scratch.

The early builds of the web client converted client code written in 1C:Enterprise script to JavaScript. The thin client works differently by converting 1C:Enterprise script to bytecode and then interpreting it on the client side. Then we implemented the same approach in the web client, which improved its performance and unified the architecture of the thin and web clients.

The first version of 1C:Enterprise platform that supported the web client was released in 2009. At that time it supported two browsers: Internet Explorer and Firefox. We also planned Opera support but an issue with application closing handlers in Opera forced us to put this plan on the back burner (we could not always detect the moment when an application was closed in order to know when to close its 1C:Enterprise server connection, and no workaround was available at that time).

Project structure

The 1C:Enterprise platform includes 4 projects that have been implemented in JavaScript:

  1. WebTools. Shared libraries used by other functionalities (these include Google Closure Library)
  2. FormattedDocument fields (implemented in JavaScript in both thin and web clients)
  3. Planner fields (implemented in JavaScript in both thin and web clients)
  4. Web client

The structure of each project is similar to that of Java projects (or .NET projects, whichever analogy you prefer). They include namespaces, each occupying a dedicated folder. Each folder contains namespace files and classes. The entire web client project consists of about 1000 files.

The web client consists of the following major subsystems:

  • Managed client application interface
    • General interface parts (system menus and panels)
    • Managed form interface that includes about 30 control types: buttons, several kinds of text boxes (for entering text, numbers, or date and time), lists, tables, charts, and other items.
  • The object model available to client developers, with over 400 types (managed interface object model, data composition settings, conditional appearance, and more).
  • 1C:Enterprise script interpreter
  • Browser extensions that provide functionality not available in JavaScript:
    • Cryptography extension
    • File system extension
    • Add-in technology that provides add-in support in both thin and web clients

Development specifics

The implementation of all the features listed above in JavaScript is quite a complex task. The 1C:Enterprise web client is probably one of the biggest client applications written in JavaScript (over 450,000 lines of code). We actively use the object-oriented approach in the web client code to simplify the development of such a big project.

At the beginning we used a proprietary obfuscator to minimize the client code size. Then, in the platform version 8.3.6 (October 2014), we switched to Google Closure Compiler. Here are the numbers that illustrate the size reduction for the web client framework:

  • Proprietary obfuscator: 1556 KB
  • Google Closure Compiler: 1073 KB

Switching to Google Closure Compiler improved the web client performance by 30%. This also reduced the amount of memory used by the web client by 15-25% (depending on the browser).

Google Closure Compiler is especially good at optimizing object-oriented code. Hence, it has been able to provide a high degree of optimization for the web client. This includes the following improvements:

  • Static type checking at the project build stage (based on JSDoc annotations in our code). The resulting static typing level is very similar to that of C++. This helps to identify a great deal of errors at the project compilation stage.
  • Code size reduction (obfuscation)
  • Some optimizations of executable code, for example:
    • Inline expansion of functions. A function call in JavaScript is a resource-consuming operation, and inline expansions of frequently used small methods greatly improve the performance.
    • Calculating constants during the compilation. If an expression depends on a constant, the constant value is substituted at the compilation stage.

We use WebStorm as the web client development environment. And for code analysis, we use SonarQube with integrated static code analyzers. They help us to detect and prevent degradation of JavaScript source code.

ef5f938a8a635202036527d017c0f23a.png

Problems: solved and being solved

During the project implementation we faced a number of challenging tasks.

Data exchange with server and between windows

In some scenarios obfuscating the source code may impact functionality. After obfuscation the names of functions and parameters in the code fragments that are external to the executable web-client code might not match the expected names. The external code includes:

  • Code received from the server as data structures
  • Code being executed in another application window

To avoid obfuscation in server interactions, we use the @expose tag:

/**
 * @constructor
 * @extends {Base.SrvObject}
 */
Srv.Core.GenericException = function ()
{
    /**
     * @type {string}
     * @expose
     */
    this.descr;
 
    /**
     * @type {Srv.Core.GenericException}
     * @expose
     */
    this.inner;
 
    /**
     * @type {string}
     * @expose
     */
    this.clsid;
 
    /**
     * @type {boolean}
     * @expose
     */
    this.encoded;
}

And to avoid obfuscation in window interactions, we use so-called exported interfaces (interfaces where all methods are exported).

/**
 * Exported interface of DropDownWindow control
 *
 * @interface
 * @struct
 */
WebUI.IDropDownWindowExp = function(){}
 
/**
 * Moves the selection one step forward or back
 *
 * @param {boolean} isForward
 * @param {boolean} checkOnly
 * @return {boolean}
 * @expose
 */
WebUI.IDropDownWindowExp.prototype.moveMarker = function (isForward, checkOnly){}
 
/**
 * Moves the selection to the beginning or end
 *
 * @param {boolean} isFirst
 * @param {boolean} checkOnly
 * @return {boolean}
 * @expose
 */
WebUI.IDropDownWindowExp.prototype.moveMarkerTo = function (isFirst, checkOnly){}
 
/**
 * @return {boolean}
 * @expose
 */
WebUI.IDropDownWindowExp.prototype.selectValue = function (){}


We used Virtual DOM before it became mainstream :)

Just like all other developers who deal with complex web UI, we quickly realized that DOM is not well suited for operations with a dynamic user interface. So we implemented an analog of Virtual DOM to optimize the UI operations. While an event is being handled, all DOM changes are stored to the memory. And it is only when all of the operations are completed that the stored changes are applied to the DOM tree.

Web client optimization

To improve the web client performance, we utilize the standard browser features whenever possible (for example, CSS). Thus, form command bars, which are present in virtually all application forms, are drawn using browser tools only (dynamic CSS-based generation).

31f7b3ed08bce69a481228d1159aff09.png

Testing

We use a proprietary tool written in Java and C++ as well as an assortment of Selenium-based tests for functional and performance testing.

It is a universal tool that works with virtually any window application, so it is suitable for testing both the thin client and the web client. The tool records all of the manual user actions to a scenario file. At the same time it records reference screenshots. New web client builds are tested automatically. Any difference between the reference screenshot and the actual look of the application window is considered a potential error. Then a QA expert decides whether it is an error or a planned change of system behavior. In the latter case the new screenshot is stored as a reference.
The tool also measures the application performance with 25-millisecond accuracy. In some cases we loop parts of testing scenarios (for example, repeatedly entering an order) to investigate performance degradation over time. All of the measurements are recorded to a log for future analysis.

Our tool and Selenium complement one another. For example, whereas Selenium does not always detect button position changes, our tool does because it performs a pixel-by-pixel screenshot comparison. Our tool also detects issues with keyboard or mouse input because it reproduces these actions itself.
Tests that run in both tools (our tool and Selenium) include typical application usage scenarios. The tests run automatically on daily 1C:Enterprise platform builds. If any performance degradation is detected (compared to the previous build), we investigate that and eliminate the cause of degradation. The rule is simple: new builds must not work more slowly that the older ones.

To investigate performance degradations, we use a variety of tools, including in particular Dynatrace AJAX Edition by DynaTrace. We record and analyze the logs for the operation being investigated on both the previous and current builds. The execution time of individual operations (in milliseconds) does not always make a difference because this can be affected by background browser operations, such as garbage collecting. Instead, we keep a close watch on the number of executed JavaScript statements, the number of atomic DOM operations, and similar things. If the number of statements or operations in a specific scenario increases, this almost guarantees performance degradation and therefore requires fixing.

Another reason for the decline in performance may be that Google Closure Compiler for some reason is not able to perform an inline-substitution of a function (for example, because the function is recursive or virtual). In these cases, we try to remedy the situation by rewriting the source code.

Browser extensions

When an application requires functionality not available in JavaScript, we use the following browser extensions:

  • File system extension
  • Cryptography extension
  • Add-in support

Our extensions consist of two parts. The first part is a so-called "browser extension" (usually a Chrome or Firefox extension written in JavaScript), which interacts with the second part—a binary extension that provides functionality implementation. We write 3 versions of each binary extension (for Linux, Windows, and iOS). Binary extensions are delivered as part of the 1C:Enterprise platform, and they are available on the 1C:Enterprise application server. The first time that a web client is installed an extension is downloaded to the client computer and installed in the browser.

In Safari, our extensions use the NPAPI technology. In Internet Explorer, they use ActiveX. Microsoft Edge does not support extensions yet, which imposes limitations on the web client functionality.

Further development

One of the development directions for the web client is adding new functionality. The web client functionality must be identical to that of the thin client. All new features are implemented in both clients simultaneously.

The other directions are architecture development, refactoring, performance and reliability improvements. For example, currently the development team is focusing on moving onward to the asynchronous model. Some part of the web client functionality still utilizes synchronous client/server interactions. Since the asynchronous model is now mainstream for browsers (and not just for browsers), we refactor the web client code to replace synchronous methods with their asynchronous counterparts. We do it in a gradual manner to give application developers time to adapt their solutions while preserving backward compatibility.