 | Level: Intermediate Wing Yung (wingyung@us.ibm.com), Software engineer, Advanced Internet Technology Group, IBM John Corwin (jcorwin@us.ibm.com), Software engineer, Advanced Internet Technology Group, IBM
01 Nov 2002 SashXB extends JavaScript with objects that wrap native functionality -- and provides all the necessary tools for writing applications from scratch. In this article, the developers of SashXB explore its inner workings and demonstrate how SashXB simplifies the development, download, and installation of applications.
While SashXB is not appropriate for all software projects, it is
perfect for small- to medium-sized network client applications. Targeted at
Web developers with HTML and JavaScript skills who want to write
full-featured native applications, as well as experienced programmers
who'd appreciate the convenience of rapid application development, SashXB
is an open source application environment that exposes native
functionality to JavaScript. It speeds the development process by
encapsulating system calls, existing libraries, and other code in easily
accessible "extensions," which are accessible as JavaScript objects.
Low-level details are hidden from application developers, who only need to
learn APIs for the new objects.
After briefly sketching out the SashXB architecture and detailing some
of its important components, we will look at several applications and step
through some sample code.
SashXB architecture
At its heart, SashXB consists of a runtime, locations, and extensions --
all of which are XPCOM components. Similar to Microsoft COM for Windows,
XPCOM is a component system developed and used extensively by the Mozilla
team that allows you to construct reusable modules. Unlike MS COM, XPCOM
is cross-platform. Interfaces are defined by IDL files and can be
implemented in and accessed from any language with proper bindings, which
currently include C++, Python, Perl, and the Java language. All of our objects are
implemented in C++.
SashXB also relies heavily on Mozilla's XPConnect, a component that
allows JavaScript code (interpreted by Mozilla's SpiderMonkey JavaScript
interpreter) to talk directly to XPCOM objects. This allows us to map
XPCOM objects into the JavaScript code's namespace so that Web applications
can access the functionality provided by the runtime, extensions, and
locations. Calls to SashXB in JavaScript code are routed to the
appropriate components.
One of the great strengths of the SashXB architecture is its
extensibility. Keeping components outside of the runtime has several
important benefits. First of all, it means that new SashXB components can
be released without having to update the runtime. It's also extremely easy
to develop a new location or extension. A developer will rarely have to
examine or alter runtime code to implement a new component. Finally, a
SashXB component only needs to be installed once; after that, any
Web application can access it. If all of the necessary components are already
present on a system, then only the Web application source files need to be
downloaded, significantly reducing download times for Web applications.
SashXB runtime
The name of a WDF (Web application data file) file is passed as a command-line
argument to the runtime. This file specifies Web application data in XML
format, including version information, dependencies, required install
files, and platform information. The runtime provides all the services
needed by a running Web application, such as dependency checking, security,
embedding Mozilla HTML rendering widgets, registry access, configuration,
and registration with the SashXB task manager. Once it verifies that
everything necessary is present, it starts a Web application by setting up and
starting the location in which the Web application will run.
Locations
Locations are responsible for loading source files and interpreting them.
Every location must implement Run and Quit methods; other appropriate
methods and properties can be defined by the developer. Currently SashXB
supports three locations: WindowApp, PanelApp, and Console.
-
WindowApp requires an HTML source file (which mostly likely contains JavaScript code containing calls to SashXB extensions), and it renders the HTML in a window through a GTKMozEmbed widget. Users can specify callbacks and various display properties for the window.
- For running applications in the GNOME panel, SashXB provides the PanelApp location. The panel length and orientation are accessible, as is the Web application's GTK widget that is being displayed in the panel. If the given source file is HTML, then PanelApp will render it through a GTKMozEmbed widget that sits inside another GTK widget in the panel. If the given source file is a JavaScript file, PanelApp will evaluate the JavaScript.
- The console location should be used for Web applications with Glade UIs or no UIs. Good examples of Web applications with no UIs include utility scripts and component functionality tests (like our registry test). Most of our Web application UIs are designed in Glade, and we run them in console locations. The source file is a JavaScript file.
 |
Extensions
Extensions extend SashXB functionality. They can wrap existing
libraries (like openldap and GTK) and existing XPCOM objects (like Mozilla's
HTTP transport object) or be written entirely from scratch (like registry
and filesystem). Every extension implements Initialize, ProcessEvents,
and Cleanup methods for starting up, handling events when there are
multiple threads, and cleaning up. Aside from simple properties and
methods, extensions can also contain constructors and callbacks.
Constructors are used to create new SashXB objects from JavaScript, and
callbacks define events that can be triggered when certain actions occur,
allowing certain functions to be called asynchronously.
Ideally, an extension encapsulates related pieces of functionality. It
would have been possible to combine access to FTP, HTTP, and LDAP in a
single network protocols extension (rather than having three separate
extensions), but they are all different enough to have their own
extensions. Similarly, we could have created three network sockets
extensions -- one for UDP, one for TCP client, and one for TCP server
sockets -- but we decided that they were so closely related in
functionality that they belonged in the same extension.
Currently SashXB supports a wide variety of extensions. Core and Linux are
always installed by default.
-
Core: Consists of six sub-parts:
-
Channels: communicate with other Web applications
-
Cursor: manipulate the cursor
-
Process: start and kill processes
-
UI: show basic UI input boxes
-
Net: open HTTP connections (wraps Mozilla's HTTP transfer objects)
-
Platform: find information about the system you're running on
-
Linux: show Linux-specific UI input boxes
-
Registry: store and retrieve values
-
Filesystem: manage files and folders
-
GTK: manipulate GUI elements (wraps GTK)
-
Glade: use Glade GUIs (wraps Glade)
-
Comm: use network sockets
-
FTP: access remote files
-
Jabber: send instant messages (wraps Jabberoo)
-
XML: parse XML documents (wraps Gdome)
-
Vorbis: play Ogg Vorbis sound files (wraps Ogg Vorbis)
-
LDAP: access LDAP databases (wraps OpenLDAP)
-
MySQL: access MySQL databases (wraps MySQL)
Security
One of our biggest concerns is security. The thought of giving a foreign,
untrustworthy Web application access to your filesystem or network is not a
pleasant one. With SashXB, a user has total control of what a Web application
is allowed to do, and the user can always deny a Web application access to
system and network resources. SashXB users can change their default
security settings, which include whether Web applications are allowed to start
processes, register XPCOM objects, and make HTTP connections. A security
setting can be a boolean (allow or disallow an action), a string
enumeration (varying levels of access, like none/local/all for the
filesystem), a number, or a string (perhaps for only allowing access to
specified Internet addresses).
Security settings are stored in a registry file (which is actually an XML
file). Each setting contains three parts: Type, Value, and EnumValue.
Type contains the type of setting it is (boolean, number, etc.). If the
type is a string enumeration, then Value contains the string array and
EnumValue is the index of the selected string. Otherwise, Value contains
the value of the security setting. Note that this flexibility makes it
easy for new security settings to be introduced. For example, the network
sockets extension allows users to specify whether or not to allow the use
UDP, TCP client, and TCP server sockets. It is generally the case that a
new extension will require new security settings.
JavaScript alone does not have access to native functionality, so security
checks are only necessary when Sash code is executed. The SashXB security
manager provides methods for querying user security settings, but it is
the responsibility of the extension to use these methods before executing
code that accesses a resource. For example, the filesystem implementation
code executes a security check on a filename before anything can be done
with it. If the user's filesystem security setting is not high enough
(perhaps the user specified local access, but the Web application wants all
access), the user will be asked whether access should be denied, allowed
once, or allowed always.
This means that SashXB users should be especially wary of untrustworthy
extensions. It's possible for an ignorant or malicious extension developer
to write code that accesses the filesystem without checking security. The
safest thing to do with unsafe code is to browse through it (SashXB is
open source) and compile it yourself.
Installer
Designed to make the process as fast and comprehensive as possible, the
SashXB installer will install a specified component or Web application by name
or URL. Rather than simply install the component or Web application specified,
it also installs all of its dependencies so that when the installation
process is complete, the installed item is ready for use. While a local
installation is possible (for users who download all the files or compile
extensions from source), most users will install new components and
Web applications off the Internet. The installer communicates with the online
SashXB locator service, which keeps track of the locations of installation
files for all available components and Web applications.
Figure 1. The graphical installer in action

Users can choose between graphical and text install modes. The graphical
installer steps through the install with the user, checking to see whether
the item is already installed, providing basic information, explaining the
relevant security settings and installation risks, and displaying any
custom install pages included by the developer.
Task manager
The task manager gives you a detailed account of which components and
Web applications are installed and which Web applications are running. All
installed items can be updated or removed with the click of a button.
SashXB keeps track of everything that has been installed in a registry
file. For every installed element, it tracks the name (Filesystem,
Registry, GTK, etc.), type (extension, location, Web application), version,
WDF location (filename or URL), associated uninstall files, and
dependencies. This information is added or updated whenever a component or
Web application is installed.
Figure 2. Showing the installed extensions

Updating a component or Web application involves checking the version of the
WDF file at the specified WDF location. If there is a newer version
available, it will be installed by the installer. Components and
Web applications can also be uninstalled from the task manager. Since the
installed files are kept track of, they can be removed from the system
easily.
Security settings can also be changed from the task manager. These are the
global security settings, which are checked when Web applications run.
Applications in SashXB
Here are several useful Web applications to demonstrate the capabilities
of SashXB: Checkers, SashFTP, Newsbar, and BluePages Lite.
Checkers
The Checkers Web application lets you play checkers with an opponent on the same
computer or across the network. You can save and load local and networked
games at any time. The Checkers source code is about 400 lines long, with
another few hundred lines of HTML to generate the game board. It uses the
Jabber (for network support) and Filesystem (for saving games) extensions,
and runs in the WindowApp location. The user interface is all done in
HTML.
Figure 3. Checkers Web application running in Linux

Figure 4. Checkers Web application running in Windows

This Web application is almost cross platform: the Linux and Windows (written
for Sash for Windows) versions differ by only about 5 lines of code due to
a minor difference in the Jabber Extension APIs; Linux and Windows
users can play against each other.
Sending a message with Jabber is quite easy, provided the user has a
default Jabber profile.
Listing 1. Sending a Jabber message
var jabber = new Sash.Jabber.Session();
jabber.Resource = "checkers";
jabber.Connect();
// ...[code omitted]...
function send_message(body) {
var message = new Sash.Jabber.Message();
message.To = opp;
message.Body = body;
jabber.SendMessage(message);
}
|
Receiving is simple as well, once you register a callback with the
extension:
Listing 2. Receiving a Jabber message
jabber.OnMessage = on_message;
// ...[code omitted]...
function on_message(session, message) {
var body = message.Body;
// code to handle moves, starting a game, receiving a saved game, etc.
}
|
Finally, let's see how Checkers saves a game. Since it stores the board as
an 8x8 array of integers, it simply writes them out to disk one row at a
time.
Listing 3. Saving a checkers game
function save_file() {
// Prompt the user for a filename.
filename = Sash.Linux.FileDialog(filename);
if (filename == "") return;
// Open a new textstream.
var file = new Sash.FileSystem.TextStream(filename,
Sash.FileSystem.MODE_WRITE);
// For verification on load.
file.WriteLine("Checkers Save File");
// Write out the board.
for (var i = 0 ; i < 8 ; i++) {
file.WriteLine("[" + board[i] + "]");
}
// Whose turn is it?
file.WriteLine(currp);
file.Close();
// Let the user know the save was successful.
Sash.WindowApp.MainWindow.StatusText = "Game saved!";
}
|
SashFTP
SashFTP is a graphical FTP client. You can queue up multiple files for
transfer, transfer entire directories recursively, and perform basic
filesystem operations (create, recursive delete, and rename) on the local
and remote filesystems. You can also bookmark sites and save the default
directories for each bookmark. It's also multi-threaded. SashFTP relies on
the FTP, FileSystem (for local file manipulation), Registry (for
bookmarks), Glade (for the UI framework), and GTK (for specific
manipulation of the UI) extensions, and runs on the Console location. It's
about 1000 lines of source. The UI was designed with the Glade WYSIWYG UI
construction tool.
Figure 5. A full-featured FTP client

First, here's the code for when the user clicks on the remote Create
button.
Listing 4. Creating a remote directory
function on_create_remote_clicked() {
// Don't perform this operation if a transfer is in progress.
if (check_transfer()) return;
// Prompt the user for the remote directory name.
var fname = Sash.Core.UI.PromptForValue("Create remote directory",
"Enter the name of the directory you wish to create:", "");
if (fname == "") return;
// Connection is the FTP connection object.
//Call its Mkdir method to create a new remote directory.
connection.Mkdir(fname);
// Redisplay the remote file listings.
populate_and_display_remote();
}
|
Here's the code for logging into the remote FTP server:
Listing 5. Logging in to an FTP server
var connection = new Sash.FTP.Connection;
connection.Connect(host, port);
connection.Login(username, password);
connection.OnLogin = "on_login";
// ...[code omitted]...
// This function gets called by the FTP extension
// when the login attempt completes.
// The arguments are an FTP connection object and a boolean.
function on_login(con , successful) {
if (successful) {
// Default data connection is binary.
con.IsASCII = false;
// Invoke progress callbacks once every second during file transfers.
con.NotifyInterval_S = 1;
// Load the default local and remote directories from the registry.
// (Directories saved when "Save Directories" is clicked.)
var lp = Sash.Registry.QueryWeblicationValue(connection_name,
"local_dir");
var rp = Sash.Registry.QueryWeblicationValue(connection_name,
"remote_dir");
// Change the remote directory.
if (rp != "") con.Cd(rp);
remote_path = con.CurrentDirectory;
// Display the file listings.
populate_and_display_remote();
display_local(lp);
// Change the title of the application to reflect the connection.
window.setTitle("SashFTP - " + connection_name);
} else {
// Failed!
status("Login failed!\n");
con.Logout();
}
}
|
Newsbar
This Web application scrolls news headlines from user-specified locations.
Clicking on a headline pops up a Mozilla window that displays the article.
Users can customize various display options, including scroll speed and
newsbar length. Users can add news sources easily by specifying an RDF file
(which contains headlines and URLs) location, and Newsbar will take care of
the rest.
Figure 6. A newsbar with configurable sources

This code starts downloading an RSS file from a specified URL via HTTP.
Listing 6. Initializing an HTTP transfer
function RefreshData() {
NewData = [];
DownloadErrors = "";
// ...[code omitted]...
for (var i in ActiveSources) {
var src = NewsSources[ActiveSources[i]];
// If there's no rss URL specified, go to the next one.
if (src.rss == "") continue;
// If there is a URL specified, open an HTTP connection to it.
var con = new Sash.Core.Net.URLConnection(src.rss);
// Set the name of the complete callback.
con.OnComplete = "OnSourceDownloaded";
// Since we'd like to download from more than one source at a time,
// make it an asynchronous download.
con.Async = true;
// Execute the download.
con.Execute();
}
// ...[stock-related code omitted]...
}
|
Once the download is complete, parse the RSS file into news headlines,
URLs, and descriptions.
Listing 7. Parsing XML
// This function is called when an HTTP transfer completes.
function OnSourceDownloaded(con) {
// ...[code omitted]...
// At this point, we know that the file was downloaded successfully
// and we know which news source it came from.
// Otherwise, parse the downloaded fss file.
var xd = new Sash.XML.Document();
// This does the actual parsing.
if (!xd.loadFromString(con.ResponseAsString)) {
DownloadErrors += "Error parsing news feed from " + source + ".\n";
return CheckDone();
}
// Locate the parent node.
var parent = xd.rootNode.getFirstChildNode("item");
if (parent.isNull()) {
parent = xd.rootNode.getFirstChildNode("channel");
if (parent.isNull()) {
DownloadErrors += "Error parsing news feed from "
+ source + ": no channel node.\n";
return CheckDone();
}
} else {
parent = xd.rootNode;
}
// Get all of the items out of the parent.
// Extract data from each one.
var items = parent.getChildNodes("item");
for (var i in items) {
var t = items[i].getFirstChildNode("title").text;
var l = items[i].getFirstChildNode("link").text;
var d = items[i].getFirstChildNode("description").text;
NewData.push(new Item(t, l, d, source, false, ""));
}
CheckDone();
}
|
BluePages Lite
BluePages Lite is a full-featured IBM employee directory search tool
(which only works inside IBM). It supports simple and advanced searches,
applies user-defined filters, and displays detailed contact information
and organizational charts. Weighing in at a hefty 3500 lines, it's our
biggest Web application by far. It uses a handful of extensions: LDAP for
search queries, Glade for the GUI (along with GTK for more control over
certain widgets), the registry for storing user settings, the filesystem
for exporting search results, and XML for representing advanced searches
and filters.
Figure 7. BluePages Lite search results and organizational chart views

Figure 8. BluePages Lite employee details view

This code sets up and makes an asynchronous LDAP request.
Listing 8. Making an LDAP search request
function ExecuteQLSearch(baseDN, scope, filter, cols){
// Create a new search request.
SearchRequest = new LDAPConnection.SearchRequest(baseDN,
Sash.LDAP.SCOPE_SUBTREE, filter, cols);
// Set the callbacks.
SearchRequest.OnSearchResult = OnQuickListResult;
SearchRequest.OnSearchComplete = OnQuickListComplete;
SearchRequest.OnError = onDefaultRequestError;
// Change the mouse cursor to display the wait icon.
// NOTE: MOUSE_WAIT was previously set to Sash.Core.Cursor.MOUSE_WAIT.
Sash.Core.Cursor.SetStandard(MOUSE_WAIT);
// Change the search button label to read "Cancel" instead of "Search".
ChangeButtonToCancel();
// Disable the Search Favorites option menu.
FavoritesSearchOption.enabled = false;
// Make sure that the connection is not null and that the correct
// maximum result limit is set.
if (LDAPConnection){
if (LDAPConnection.SearchMaxResults != BluePagesMaxResults){
LDAPConnection.SearchMaxResults = BluePagesMaxResults;
}
}
// Invoke the request.
return (SearchRequest.Invoke());
}
|
This code handles LDAP results as they are returned.
Listing 9. Handling an LDAP result
// This gets called every time a result returns.
function OnQuickListResult(searchRequest, entry){
// Keep track of the total number of results.
QuickListSearchResultCount++;
// Display a message on the status bar.
Status(QuickListSearchResultCount + " results found.");
// ...[code omitted]...
// At this point, all of the quick list values have been retrieved from the
// returned entry and put into values, which is an array.
// The first element of the values array is the UID, which we don't
// want to display. We add it to an array to track the UIDs of the displayed
// entries, then put the remaining values into a row of the columned list.
if (values && (values.length > 0)){
QuickListUIDs.push(values[0]);
SearchResultsCList.Append(values.slice(1));
}
}
// This gets called when a search is complete.
function OnQuickListComplete(searchRequest, entries, success, description){
// Restore the mouse cursor.
Sash.Core.Cursor.SetStandard(MOUSE_DEFAULT);
// Change the label of the search button back to "Search".
ChangeButtonToSearch();
// Re-enable the Search Favorites option menu.
FavoritesSearchOption.enabled = true;
// Print a message to the status bar.
if (success){
Status (QuickListSearchResultCount + " results found. Search complete.");
} else {
Status (description);
}
QuickListSearchResultCount = 0;
}
|
 |
Limitations of SashXB
Of course, SashXB is not appropriate for writing any type of application.
JavaScript is mainly used for checking the number of digits in zip codes
and phone numbers, not for developing large software projects. BluePages
Lite, the SashXB IBM employee directory navigator, is approximately 3500
lines of JavaScript code, and it definitely pushes the limits of SashXB's
capabilities. It's quite difficult to manage that much JavaScript code. In
addition, we imagine that naively-written, computationally-intensive
Web applications would perform poorly because JavaScript is an interpreted
language. Fortunately, any expensive computations could be encapsulated in
an extension that would probably be written in C or C++.
SashXB is perfect for small- to medium-sized client applications or
command-line utilities. It provides access to common system resources and
network protocols, as well as tools for creating GUIs. Extensions for
nearly any type of desired functionality can be written; new extensions
will open up even more opportunities for developers.
Resources - Learn more about Mozilla, XPCOM, or XPConnect.
- Related articles on developerWorks include:
- Find out more about some of the other open source technologies mentioned in this article:
- Find more Linux resources in the developerWorks Linux zone.
About the authors  | |  | Wing Yung is a Software Engineer in the Advanced Internet Technology group at IBM in Cambridge, MA. He has been working on SashXB for the past year. He
graduated from Harvard College with a degree in computer science. You can contact him at wingyung@us.ibm.com. |
 | |  | John Corwin is a Software Engineer in the Advanced Internet
Technology Group at IBM in Cambridge, MA. As one of the original SashXB team members, he has been
around since the project started during Extreme Blue 2000. He graduated from CMU. You can contact him at jcorwin@us.ibm.com. |
Rate this page
|  |