Brackets is a code editor for HTML, CSS and JavaScript that is built in
HTML, CSS and JavaScript. That makes it a fully featured desktop
application written using Web technologies. Brackets embeds a browser
to show and run the entire editor and extends it with
additional functionality used by JavaScript. There are more and more
applications using this kind of mixed platform - HTML/JavaScript becomes
first class User Interface (UI) (and not only) for Window 8 applications, other tools from Adobe such as
Edge Animate,
Icenium - a new cloud IDE, recently announced by
Telerik.
Sencha Animator also is an HTML5 application, running inside QT WebKit. Even the UI of most browsers is written in HTML.
This approach has several advantages:
- fully cross-platform UI - as long as the browser is running on a platform,
the UI is going to be running on that platform too
- full UI customization using CSS
lots of available application frameworks MVC, MVVM - Backbone.js,
knockout.js, you name it
- lots of UI libraries - Kendo UI, jQuery UI, mochaui
- easy development of the UI using Chrome Dev Tools, Firebug, etc.
- Live reload of the UI, without restarting the whole application!
- native access to filesystem and everything without sandboxing
- use existing native APIs
- native performance where necessary
- only single browser to support - this is a huge relief for the HTML/JavaScript developers
Given the advantages, I am sure that more and more mixed native and HTML applications are going to appear.
Brackets Architecture
Brackets consists of two applications running together - an
HTML/JavaScript application and a native
C++ shell
that runs the HTML application. The native shell consists of two
processes - one running the HTML application inside a browser, and one
running the heavy native part like filesystem operations and number
crunching. The communication between the two processes is asynchronous,
using JavaScript callbacks and request / response for the native code.
- The shell extends the standard HTML DOM with the following functions:
- Open file or directory using the OS dialog
- Read file
- Write file
- Create directory
- Rename file or directory
- Get file last modification time
- List directory contents
- Open and close browser for Live Preview
- Open extensions directory in OS file browser
- Open developer tools for brackets itself
- Get the system default language
- Time since the application start
- Get the application support directory
Brackets Shell implements the asynchronous communication between the
HTML render process and the browser in a very simplistic way: JavaScript
executes a function
appshell.fs.readdir(path, callback), the
render process stores the callback in a mapping from a request id to
callback and calls the native process with the name of the function.
When the native process is ready it sends back any result of the call
together with the request id. The HTML process finds the callback by the
request id and executes it with any result.
This architecture is the same as for any good GUI application - the UI never executes any expensive functions and is always responsive and all data manipulation is in a separate thread.
Running Brackets in Coherent UI
Making
Brackets run inside
Coherent UI is really easy. We start by
creating a view that loads
www/index.html relatively to the executable.
To support
Coherent UI we have to include some JavaScript files in the index.html
of
Brackets
These scripts are
Coherent UI dependencies,
Coherent UI itself and the abstraction layer between all JavaScript code of
Brackets and
Coherent UI.
Then we have to register our native callbacks for the asynchronous
calls:
Brackets native functions always return an error code as first argument
to the JavaScript callback. This mechanism can be implemented in
Coherent UI, but then the callbacks always have to do two things -
handle the correct result and handle the error, which is kind of
annoying.
engine.Call might take two callbacks
- one for the successful result and one for error. Therefore we have to
wrap the normal callback in an object with separate handlers for
success and error.
gist: javascript callback wrapper
All that is left now is to wrap the callbacks and use
engine.Call
instead of
native function.
and to write the native function
Handling synchronous calls
Brackets has and some synchronous methods that return to JavaScript
immediately. These methods are:
- Get the application support directory
- Get the system default language
- Time since the application start
Coherent UI does not support providing synchronous JavaScript
functions by design, so we will have to work around that.
The application support directory remains constant through a single run of
Brackets, so we can set it once and for all during application initialization:
Getting the system default language might be implemented in the same way.
The last method left is time since the application start and is used only in
Show Performance Data menu. This method might be implemented entirely in JavaScript, assuming
Brackets is not going to be reloaded during the performance test run.
Porting Bracket to Linux
Since Coherent UI already runs on Linux and we have implemented most of the native
functions using the cross-platform
boost::filesystem library, all we
have to do to get is
showing an open file dialog, creating and closing a chrome instance
opening an URL and folder in the default OS HTML browser and file manager.
We use the
GtkFileChooserDialog and the
xdg-open tool.
Unfortunately, the
Live Preview doesn't work under Linux.
Live Preview in
Brackets creates a new
Google Chrome instance with
enabled remote debugger, attaches to the debugger using XmlHttpRequest
and WebSockets and controls the instance via the debugger. What happens on Linux
is that one of the XmlHttpRequests fails with "DOM Exception" and the
debugger is unable to attach to the instance.
Another Linux related issue is that I couldn't find a way to close a
Google Chrome tab on Linux gracefully, so when the developer tools are closed
you get the
"Ow, Snap" page. In a future version we will stop using
Google Chrome for
Live Preview and for showing the developer tools, which will fix this problem.
Here is a short video of Brackets running on Linux:
Get a
prebuild package or get
Coherent UI and start hacking!