Chapter 12. Remote Applications
Remote applications developed with Mozilla use an application without having to endure a full download process. Given the fundamental similarities of Mozilla and standard web content, a remote Mozilla application can work much like a regular web page. For example, you can point people to your project at http://www.foobar.com/myApp.xul, and if they use Mozilla (or a browser that is built with Mozilla, such as Netscape 7), the browser window becomes the application. Serving an application in this way allows you to use most features of a locally installed Mozilla program and gives you unique options that aren't otherwise available. These options are discussed in this chapter.
This chapter explores this alternative distribution method and compares it to how an installable application is built. Generally, there is no difference between these two types of applications other than how they are delivered. However, you should be aware of the difficulties encountered when using remote applications.
One of the most important aspects of remote applications for Mozilla is the XPFE environment, or more specifically, the use of XUL/XBL, JavaScript, and CSS. Using the XPFE in remote application development offers more possibilities than, for example, just embedding Gecko into your application. It is the focus of this chapter.
12.1. Directions in Remote Application Development
Currently, remote Mozilla applications are not prevalent because development focuses on making the client applications as stable and efficient as possible. Therefore, this area of Mozilla development is largely speculative. This chapter argues that remote applications are worth looking at more closely.
One advantage a remote Mozilla application has over a client application is that a developer doesn't have to worry about an installer. Also, all users have access to the latest version of your application. Because the application is stored centrally on a server instead of on the local computer of everyone who tries your program, when you update it, you make an update available to all users.
Remote software applications might be hosted in a centralized point on the network, on multiple nodes, or on any random node in a Peer to Peer (P2P) fashion. It's even possible to have a whole suite of remote Mozilla applications hosted on several computers combined into one coherent package. Figure 12-1 shows one scenario for a simple distributed Mozilla application.
Currently, one of the remote application's biggest disadvantages is that it has very restricted JavaScript privileges. Here, privileges refer to the ability to carry out certain functionalities on the local system. As many high-profile “worm” viruses emerge routinely these days, security restrictions on downloadable scripts and applications are understandable. Some of the most high-profile malicious scripts access the local file system. This is not a problem unique to the Mozilla environment, but it is something to be aware of when planning and implementing a remote application.
To improve security, Mozilla automatically limits what JavaScript has access to on your computer when the executed scripts come from a computer other than the local one. One workaround uses signed scripts, as described in the Section 12.6 section later in this chapter. You can also have users set a special preference to enable universal XPConnect privileges to both local and remote files. To learn how to open up the security sandbox in this way, see the section Section 12.7 later in this chapter.
12.2. Basic Remote Application Example
The simple XUL file in Example 12-1 uses the user's local skin information to create a toolbar-type interface for a file that can be loaded from the server. This successful effect depends on whether your server is configured with the correct Multipart Internet Mail Extension (MIME) type (see the later section Section 12.3.1). The id on the buttons are taken from the navigator skin, so the look of the remote file changes when the user switches themes and remains consistent with the look of the browser itself.
Example 12-1. Remote XUL example
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://navigator/skin/" type="text/css"?>
<window id="remote_example"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Simple Remote Example">
<hbox>
<button label="XFlies" class="button-toolbar" id="page-proxy-button"/>
<button label="Reptiles" class="button-toolbar" />
<button label="Bugs" class="button-toolbar" />
</hbox>
</window>
As you can see in Example 12-1, the markup of a remote XUL file is like that of a XUL file that is part of a local system's installed application. Figure 12-2 shows the XUL file presented using both Classic and Modern themes.
The XUL in Example 12-1 is minimal, but it does show that the chrome:// URLs are accessible to the remote file and that the CSS and image resources available in the chrome's skin subdirectories can be accessed remotely. The image on the first button is picked up from a local JAR file, as accessed through chrome by using the button's page-proxy-button id. A more elegant application would use toolbars, menus, and other widgets to create a full-featured application UI.
12.2.1. Case Study: Snake (a.k.a. Hiss-zilla)
In this section, we look at an application that is stripped down and based on a basic concept but still useful. This application shows the potential of remote application development. This case study discusses a full-featured game that is played over the Internet. Figure 12-3 below shows a sample screenshot of Hiss-zilla.
A direct link to a remote XUL file provides access to the game, as seen in the location bar of the browser in Figure 12-3. The game's rules are straightforward. Click on the New Game button or use N on the keyboard to begin, use arrow keys to change direction, and use the character P to pause the game. To play a game of Hiss-zilla or to take a closer look at the code, see the project page http://games.mozdev.org/action/snake/.
The complete package includes all files associated with an XPFE application, including a XUL file, a stylesheet, images, and JavaScript files. The files and their descriptions are as follows:
snake.xul
Contains the window definition and the top level of the application with the game grid, visual text, and application buttons.
snake.js
Contains the functionality for the game including the snake's movement and the eating of the food.
snake.css
Contains styling for the UI features and inclusion of some images.
screen.js
Enables full screen mode in the game.
Image files
Miscellaneous images that represent parts of the snake's body as it moves in different directions and the food that it eats.
The Snake application will be developed further later in the chapter in the context of signed scripts. You will see new features that allow you to run the game in full-screen mode and to store the scores. These features illustrate different concepts relevant to remote applications.
Mozilla Gaming
Hiss-zilla is not the only example of a game created with Mozilla. Others, such as Mozinvaders, Mozteroids, PAGMAN, and Xultris use JavaScript and Mozilla's rendering engine to recreate two-dimensional arcade games from the 80s and early 90s. Links to most games are available at http://games.mozdev.org/.
Many of these games were created to see how far the application development capabilities of Mozilla could be pushed. PAGMAN in particular was designed as a test case to see what was possible; the result was almost identical to the original PacMan game. The creation of PAGMAN was documented in an article that provides more information about how the game came about and was developed. You can find the Building a Game in Mozilla article at http://www.oreillynet.com/pub/a/network/2000/06/30/magazine/mozilla_game.html.
Although all of these games are freely available as open source projects, not all of them work with Mozilla 1.0. Many were created while Mozilla was still in development, so games that worked on pre-1.0 releases of Mozilla need additional development to work today. The good news is that if you are just dying to play Mozteroids or Xultris, you can take what you have learned in this book and update the projects so everyone can enjoy them.
12.3. Setting Up XPFE for Remote Applications
Remote Mozilla applications are limited because they cannot access and use some of the rich functionality provided in XPCOM interfaces. Unless you make the privilege change described in this section, access to XPCOM via XPConnect in your web page or remote application is forbidden. This privilege modification set up by the remote application developer grants complete access to the Mozilla functionality, to resources in the chrome, and to the host system via the Mozilla components that handle such tasks as File I/O. Of course, making this change means that the files on your server could be read, written to, or deleted, which is why rights are restricted by default. We recommend that you grant this extended privilege only when you do not have valuable data on the server or if you have taken steps to ensure that the data cannot be accessed.
The privilege, called Universal XPConnect, can be turned on from the Privilege Manager, which is a property of the netscape.security object. Example 12-2 shows a script that turns this privilege on and then uses new found privilege to create XPCOM component instance.
Example 12-2. Enabling universal XPConnect
<script type="application/x-JavaScript"> netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var Simple=new Components.Constructor("@mozilla.org/js_simple_component;1", "nsISimple"); var s = new Simple( ); for(var list in s) document.write(list+"<br>\n"); </script>
You can also turn on this privilege in your profile user preference file with the following line:
enablePrivilege("UniversalXPConnect");
A script with this kind of plate-cleaning power can only be run successfully when it's executed locally, as from a local XUL file with a <DEFANGED_script> element. To open up XPConnect remotely with JavaScript like this, you have to use a signed script (see the section Section 12.6 in this chapter).
Once this privilege is enabled, remote XUL applications can run as if they are local. Remote files can use any Mozilla component, reuse skin resources, XBL widgets, and whatever else the browser uses.
12.3.1. Server Configuration
Proper configuration of the XUL MIME type on your web server is necessary to serve remote Mozilla applications successfully. The trick is to ensure that the server recognizes the XUL file type and knows how to serve it properly. By default, most web servers serve files with such unrecognized extensions as text/plain. By adding the type application/vnd.mozilla.xul+xml to your server's configuration, you can make sure that the server matches the file extension and sends this MIME type in the HTTP header to the browser. To serve up static XUL pages from your web server, you need to add this line to your mime.types file if it hasn't been added already:
application/vnd.mozilla.xul+xml <TAB> xul
This is how you can configure Apache MIME types to serve static XUL pages. Note that the mime.types file requires that you separate the type from the suffix. The format is:
mime type <tab> extension.
After the type is added to the server, the browser recognizes this header as an XUL file and parses, renders, and creates the appropriate DOM. If your server isn't configured to recognize this MIME type, then users see the contents of your file as source only -- a plain text complete with all the formatting.
Now that your web server is configured correctly, you can add a sample XUL file (such as the file in Example 12-3) to your web site to make sure things are running properly. You should name the file remote.xul and save it in your web site's root directory for testing.
Example 12-3. A sample static XUL file
<?xml version="1.0"?> <!DOCTYPE window> <window id = "remote" xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title = "A Remote Image" style = "min-width:282px; min-height:137px;" orient = "vertical"> <image src="http://books.mozdev.org/screenshots/logo5.gif" /> </window>
You can now view this window by launching it as a chrome file from Mozilla as follows:
./mozilla -chrome http://my.domain/remote.xul
Or you can also load it up by simply entering the URL in your browser location bar as follows::
http://my.domain/remote.xul
12.4. Generated Content
Today part of the Web is driven by dynamically generated content. This content is primarily stored in databases, which is used by an application or middle layer to format the data to HTML. The web server then sends the content to the browser. The browser receives the web page as the end result and has no knowledge that the page was generated on the server -- just that it is a properly formatted HTML file. By using these same conventional principals, we can send generated or database-driven XUL, CSS, or JavaScript to Mozilla. Because XUL is a document that creates a UI, this widget drawing capability opens up a whole new world to web application development.
If you are a web developer creating database-driven applications, you will quickly see the limitations of using a simple markup like HTML -- for example, being constrained to the content area of a browser for which you cannot manage all of the application interface's “real estate.” Using a browser window for your application is a common enough practice, but you still don't have the application-level widgets of most client-side applications when you use HTML. This section shows that using XUL files created by scripting languages allows you to create windows and applications that move out of the browser window and into the full-featured application space. Mozilla's origins, after all, are as a web browser and suite of Internet applications, and the latest technologies in the XPFE toolkit go that extra step to allow the presentation of UI information over the wire.
12.4.1. Generating Content with Scripting Languages
This section discusses the basic mechanics of server-generated content. In most cases, the actual content is static -- although the server application creates the page dynamically, the content itself is not input. It is also not created dynamically by other methods, such as using JavaScript to manipulate the client's document after it loads from the server.
To generate server-generated content, you need to use a scripting language. We explore the use of three different options: PHP, Perl, and Python. PHP is probably the most natural language for this application because it has its origins in serving up dynamic HTML, but you can play around with your favorite language and determine whether it has the appropriate capabilities for this environment. To use various scripting languages with Mozilla, you need a working knowledge of their capabilities; the scope of this book doesn't provide programming information for the selected scripting languages.
12.4.1.1. PHP
When users of your application may not have configured their browser to support the correct MIME type, you can use the PHP header function to send the browser the correct type.
Remember that when using XUL with PHP, you need to edit your php.ini file on the server and change the default configuration to:
short_open_tag = Off
By default, this configuration is set to “On.” When “Off,” this setting tells the PHP interpreter to parse only the files with escape tag identifiers, such as <?php ?> (and not <? ?>, which are used by XML). This process is separate from the XML parsing process that occurs when Mozilla receives and tries to render the generated content. If you don't change the .ini file, you will see an error like this:
Parse error: parse error in /usr/local/share/doc/apache/remote_xul.php on line 2
This error occurs when PHP sees your XUL syntax as invalid PHP code.
Once PHP is properly configured to handle XUL files, you're ready to go. To start out with something simple, use the code in Example 12-4 to produce a simple XUL window with the xFly logo by using PHP. Be sure to save the file with a .php extension.
Example 12-4. Using PHP to generate the correct XUL MIME type
<?php header( "Content-type: application/vnd.mozilla.xul+xml" ); ?>
<?xml version="1.0"?>
<!DOCTYPE window>
<window
id = "remote"
xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title = "A Remote Image"
style = "min-width:282px; min-height:137px;"
orient = "vertical">
<image src="http://books.mozdev.org/screenshots/logo5.gif" />
</window>
Also remember that a space below the PHP tag results in a parse error in Mozilla. The next example shows the PHP header and the XML declaration at the start of the PHP file. The space between the two renders it invalid:
<?php header( "Content-type: application/vnd.mozilla.xul+xml" ); ?> <?xml version="1.0"?>
After PHP parses its content on the server, the rest of the document is sent to Mozilla on the client. Put this file (remote_xul.php) somewhere in your document root on the server with PHP installed and launch it from Mozilla like this:
./mozilla -chrome http://my.domain/remote_xul.php
The window defined in Example 12-4 now appears, displaying the image. You can take advantage of this relatively straightforward technique to serve up more feature-rich user interfaces, inclusive of buttons, menus, and any other pieces of XUL that are needed.
12.4.1.2. Perl
Although PHP is rising in popularity, Perl is still a very popular web-scripting language. Perl is often used to drive back end web applications with CGI scripts. To process Perl scripts, no extra configuration is needed once you have the Perl interpreter set up to run in a web server environment. If, for example, you're already using Perl to serve up dynamic HTML pages, you're all set. Otherwise, you should grab a distribution of Perl (http://perl.com/) and set up the paths to the binary files in your server scripts. This procedure is done by placing the path in the header, otherwise known as the shebang line. This script usually takes a form similar to #!/usr/local/bin/perl or #!/usr/bin/perl. You must also make sure that the server knows where the Perl executable is, which involves including the path to it in the systems PATH environment variable. Depending on the platform and the web server you use, other environments may need to be set.
Example 12-5 shows a simple CGI script that generates a minimal amount of XUL for display in the browser. Perl is useful for this task because you can set up several possible scripts and call selected ones, depending on what choices the user makes. You can even have different forks in the same script that displays different widgets or data. For example, imagine that your remote XUL application is an online travel planner. You would display maps, information, and links to resources based on the user's geographic location or preferences for destinations they entered earlier.
Example 12-5. A simple Perl-generated XUL file
#!/usr/bin/perl print "Content-type: application/vnd.mozilla.xul+xml"; print qq{ <?xml version="1.0"?> <!DOCTYPE window> <window id = "remote" xmlns = "http://www.mozilla.org/keym style = "min-width:282px; min-height:137px;" orient = "vertical"> <image src="http://books.mozdev.org/screenshots/logo5.gif"/> </window> };
In Example 12-5, the MIME type must be specified as part of the first line in the CGI script after the shebang line, and rest of the script is the XUL code used by Mozilla. Although this example does not display real dynamic content (such as the kind you get from CGI forms or other user-supplied data), it shows how you can interpolate dynamic data into a simple CGI application model.
12.4.1.3. Python
Like Perl, Python provides modules that make it easy to create CGI applications that generate content dynamically. Python is already an important language in the Mozilla environment because of the PyXPCOM bindings discussed in Chapter 8.
If Python is a language that you like to code in, using it in a remote XUL environment would also make sense. Python combines the features of a lower-level language like object-oriented class and function design with the flexibility and simplicity of a scripting language. The latter feature (ease of use) is relative to a language like C++. Example 12-6 shows how to use Python to create a simple form consisting of three checkboxes.
Example 12-6. A Python-generated dynamically updated form
#!/usr/local/bin/python import cgi form = cgi.FieldStorage( ) print """Content-type: application/vnd.mozilla.xul+xml\n <?xml version=\"1.0\"?> <!DOCTYPE window> <window id = "remote" xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title = "listbox" style = "min-width:282px; min-height:137px;" orient = "vertical"> <box>""" print ' <checkbox label="%s" />' % form['op1'].value print ' <checkbox label="%s" />' % form['op2'].value print ' <checkbox label="%s" />' % form['op3'].value print """</box> </window>"""
In this example, the CGI module is loaded with the Python import statement, and the form object is initialized so that data input to the page in the form of URL ?name=value pairs create the XUL content dynamically.
Example 12-6 takes a URL, such as http://www.brownhen.com/cgi-bin/xulgen?opt1=peter?opt2=paul?opt3=mary, and displays a checkbox for each value of the named form fields. The content type is printed before the XUL content to tell the web server what to pass when the script produces its output.
12.4.2. Generating Content from a Database
One of the important facets of dynamically generated content is interaction with a database to store and retrieve values for the user. For example, a public forum or subscription-based service could have a client that is written in XUL and requires authentication of users. Having such a client could require some form of database lookup. This section covers a simple example that uses data stored in a database to generate a XUL tree. We will use PHP to retrieve the data from the SQL-driven database and format it into XUL. Theoretically, this database could be any relational model, such as Oracle or MySQL.
Example 12-7 generates a simple tree listing with the columns “User” and “Project.” The SQL script creates a table called “sample” and then inserts value pairs into the table. To use this type content generation, your database must be set up to handle table creation and dynamic updating via SQL calls.
Example 12-7. SQL script with User and Project data
# table to be inserted into database CREATE TABLE sample ( User char(16) NOT NULL default '', Project char(32) NOT NULL default '' ); INSERT INTO sample VALUES ('Bob','moz_bob'); INSERT INTO sample VALUES ('Joe','skinner'); INSERT INTO sample VALUES ('Bret','bretzilla'); INSERT INTO sample VALUES ('Sally','mozstream');
The code in Example 12-7 creates a table with two fields, “User” and “Project.” It then inserts four records into the newly created table in the database by using INSERT INTO calls. Now the script has tangible data to query.
Example 12-8 shows the creation of a simple XUL tree with PHP and a MySQL database. The “User” and “Project” columns display the data in the table.
Example 12-8. XUL generated from database
<?php header( "Content-type: application/vnd.mozilla.xul+xml" ); ?> <?xml version="1.0"?> <?php // connect code $host = "127.0.0.1"; $user = "nobody"; $database = "test"; $password = "mypass"; $connect = mysql_connect($host, $user, $password); mysql_select_db($database); $query = "SELECT * FROM sample ORDER BY User"; $result = mysql_query($query, $connect); $e = mysql_error( ); if($e) print "ERROR:projects: $e"; $row = mysql_num_rows($result); ?> <!DOCTYPE window> <window id = "remote" xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title = "A Remote Image" style = "min-width:282px; min-height:137px;"> <image src="http://books.mozdev.org/screenshots/logo5.gif" /> <hbox> <tree> <treecols> <treecol id="userCol" label="User" /> <treecol id="projectCol" label="Project" flex="1"/> </treecols> <treechildren> <?php // generate data from db
for($i=0;$i<$row;$i++) { $user = mysql_result($result, $i, "User"); $project = mysql_result($result, $i, "Project"); print "<treeitem container=\"true\" open=\"true\">\n"; print "<treerow>\n"; print "<treecell label=\"".ucwords($user)."\" />"; print "<treecell label=\"".ucwords($project)."\" flex=\"1\"/>"; print "</treerow>\n"; print "</treeitem>\n"; } ?> </treechildren> </tree> <spacer flex="1" /> </hbox> </window>
The PHP header method is placed at the top of the file to properly format the output as XUL. The PHP MySQL APIs prepare the code for connection to a MySQL database. Finally, a loop is used to print out the stored data's treerows and treecells.
This kind of operation provides insight into the possibilities of using remote XUL when you have information stored in a database. Many web applications already do this via HTML forms and other mechanisms, but Mozilla's XPFE toolkit provides a richer widget set than HTML to display and retrieve information (see the section Section 3.6).
Some XUL widgets are created specifically to display complex tabular data such as <tree>, and some widgets provide user selection such as <checkbox>, <radio>, and <textbox>, plus the CSS used for controlling presentation. All of these widgets can be generated on the fly from the database and used to accept user-inputted information.
12.4.3. Localizing Remote Applications
Unfortunately, localizing remote applications is a not as straightforward since there is no http: protocol equivalent for chrome:-included locales. You could use HTTP/1.1 content negotiation to serve the users-preferred language, but Mozilla 1.0 does not read DTD files over the wire. To overcome this problem, use server-side page processing. For example, this code includes the DTD by using PHP:
<!DOCTYPE window [ <?php require(PROJECT_PATH."/online/locale/xfly.dtd"); ?> ]>
Therefore, the served page looks like this:
<!DOCTYPE window [ <!ENTITY fileMenu.label "File"> <!ENTITY fileMenu.accesskey "f"> ... ]>
The only caveat for this approach is that you need a method to filter the entities, depending on which language is loaded. Obtaining a method could be done by reading in the locale and outputting the entities or by calling a separate script. This overhead is not necessarily high, as multiple files exist for multiple languages in a client distribution. A remote application would require the same process, but in a different format.
12.5. Certificate Authorities and Digital Signatures
Instead of changing the Universal XPConnect privileges (see Section 12.3 earlier in this chapter), you could create signed remote applications that can be granted access to users' computers. A signed application means that the application has a digital signature, which verifies that a file or group of files was created by the person or organization from which you download and that they are trustworthy. In essence, if you trust the person or organization signing the files, then you trust the files themselves.
Digital signatures originate from a certificate authority (CA), an organization that claims responsibility for any digital signature it creates. CAs act as gatekeepers by allowing only people who the organization trusts to create digital signatures. Large CAs like Verisign, whose certificates come preinstalled in many web browsers, enforce validity through large fees. For example, if you can afford $600, then you are an organization with whom the CA would be glad to associate. That $600 then also buys your application respectability with user's web browsers. You can see the CAs that come with the Mozilla browser by going to Privacy & Security > Certificates in your preferences panel and then by selecting the Manage Certificates option. Of the different types of CAs -- there's a type for SSL connections, for example, and another one for S/MIME -- the Netscape Object Signing certificate is what matters for signed applications.
Fortunately, to get your remote applications signed by a CA, you don't have to pay for a Verisign Netscape Object Signing CA because other options are available. You can use the MozDev CA, for example, and even create your own. The next section tells you how use Mozilla tools to become your own certificate authority so you can sign your own applications and those of other Mozilla developers. The Section 12.6 section later in this chapter uses the MozDev CA to discuss both avenues.
12.5.1. Mozilla Network Security Services (NSS)
The Mozilla Network Security Services tools, which are described in detail at http://www.mozilla.org/projects/security/pki/nss/, allow you to become your own Netscape Object Signing CA. By becoming your own Netscape Signing CA, you can distribute signing certificates to Mozilla application developers. You can obtain the tools via a simplified distribution of NSS for Windows and Linux at http://certs.mozdev.org. These tools allow you to become a CA and to package signed remote Mozilla applications. Finally, the commands for CertUtil work the same way on Windows, Linux, and any other OS on which you run CertUtil.
12.5.2. CA Certificates, Signing Certificates, and the Certificate Chain
A certificate represents an organization in a official digital form. It provides fields for the organization's name, contact information, date of issue, and an expiration date. There are also two keys: a private and a public key.
To become a CA, you need to create two certificates for yourself: a “root certificate” and a “distribution certificate.” Once you set up these certificates, you can issue signing certificates, which create digital signatures. Figure 12-4 shows the chain of relationships between certificates.
The distribution certificate is based on the root certificate's public key. You can then create signing certificates from the distribution certificate, using its public key that it handles automatically by NSS. Finally, the digital signatures derived from the signing certificate can sign applications using NSS.
12.5.3. Setting Up a Certificate Authority
Using the CertUtil tool that comes with NSS, you need to create a root certificate and a distribution certificate for yourself to become a CA. The CertUtil tool is located in the NSS installation's bin directory and can be run from anywhere in a console window. In the next few sections, we walk through the steps necessary to accomplish this process.
12.5.3.1. Creating a certificate database
Mozilla comes with a prefilled certificate database that contains information about certificates from Verisign and other CAs (found in the file cert7.db in the user profile directory), which you can modify. However, starting with a blank database is better because it avoids the possibility of corruption.
At the prompt, create a new database by using the -N and -d options for CertUtil:
C:\NSS\bin>certutil -N -d .
Enter a password for the database. You need to reenter this password every time you issue or change certificates. CertUtil creates the files cert7.db, key3.db, and secmod.db in the same directory as CertUtil.
12.5.3.2. Creating the root CA certificate
The root certificate is the foundation for the certificate chain and should not be shared with anyone outside your organization. You may want to consider storing the database or exported certificate on a floppy disk for safe keeping. If it gets lost or stolen, you need to make a new one and all your users will need to use new certificates and resign their applications.
Once you create cert7.db, CertUtil can process the Root CA Certificate into it, using the following and substituting the name of your CA for mozdev.org:
C:\NSS\bin>certutil -S -s "CN=mozdev.org, O=mozdev.org" -n "mozdev.org" -t ",,C" -x -d . -1 -2 -5
Enter a password at the prompt and proceed by making the menu choices shown in Example 12-9.
Example 12-9. Creating a root certificate
Generating key. This may take a few moments... 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signing key 6 - CRL signing key Other to finish 5 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signing key 6 - CRL signing key Other to finish 9 Is this a critical extension [y/n]? n Is this a CA certificate [y/n]? y Enter the path length constraint, enter to skip [<0 for unlimited path]: Is this a critical extension [y/n]? n 0 - SSL Client 1 - SSL Server 2 - S/MIME 3 - Object Signing 4 - Reserved for futuer use 5 - SSL CA 6 - S/MIME CA 7 - Object Signing CA Other to finish 7 0 - SSL Client 1 - SSL Server 2 - S/MIME 3 - Object Signing 4 - Reserved for futuer use 5 - SSL CA 6 - S/MIME CA 7 - Object Signing CA Other to finish 9 Is this a critical extension [y/n]? n
The mozdev.org Root CA Certificate resides in cert7.db. You can export it to a file for safekeeping as follows:
C:\NSS\bin>certutil -L -d . -n "mozdev.org" -a -o mozdev.cacert
This code will yield an ASCII representation of the certificate.
12.5.3.3. Creating a distribution CA certificate
Next you must create a distribution certificate. This certificate will be installed into the user's Mozilla web browser so Mozilla can verify related signed remote Mozilla applications. Start the certificate by typing the following code at the prompt and substituting the name of your CA for “mozdev.org”:
C:\NSS\bin>certutil -S -n "certs.mozdev.org" -s "CN=certs.mozdev.org, O=certs.mozdev.org" -c "mozdev.org" -v 96 -t ",,C" -d . -1 -2 -5
Enter a password at the prompt and make the menu choices shown in Example 12-10.
Example 12-10. Creating a distribution certificate
Generating key. This may take a few moments... 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signning key 6 - CRL signning key Other to finish 5 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signning key 6 - CRL signning key Other to finish 9 Is this a critical extension [y/n]? n Is this a CA certificate [y/n]? y Enter the path length constraint, enter to skip [<0 for unlimited path]: Is this a critical extension [y/n]? n 0 - SSL Client 1 - SSL Server 2 - S/MIME 3 - Object Signing 4 - Reserved for futuer use 5 - SSL CA 6 - S/MIME CA 7 - Object Signing CA Other to finish 7 0 - SSL Client 1 - SSL Server 2 - S/MIME 3 - Object Signing 4 - Reserved for futuer use 5 - SSL CA 6 - S/MIME CA 7 - Object Signing CA Other to finish 9 Is this a critical extension [y/n]? n
Note the differences between this process and that used to create the root certificate. The distribution certificate is not self-signed, so to create it, you must reference the mozdev.org Root CA Certificate in the initial command. Also, the -v 96 option indicates that the certificate is good for 96 months.
Once you've created the distribution certificate, export it using the following command (for which “certs.mozdev.org” is the name of your CA):
C:\NSS\bin>certutil -L -d . -n "certs.mozdev.org" -a -o certs_mozdev.cacert
Keep track of the resulting file because you will need to upload it to your web site later so browsers can install it.
12.5.4. Issuing Signing Certificates
Once you create the two root and distribution certificates for your organization, you are a certificate authority, much like VeriSign. You decide who gets the privilege of a signing certificate and issue them accordingly. Signing certificates should not be reused for different people. When you want to give out a signing certificate, you should create a new one by using CertUtil and make a copy of the exported certificate for yourself to keep and catalog.
To create a signing certificate, use the following command and substitute the name of your CA for “mozdev.org”:
C:\NSS\bin>certutil -S -n "certs.mozdev.org/signing" -s "CN=certs.mozdev.org/signing, O=certs.mozdev.org" -c "certs.mozdev.org" -v 96 -t ",,C" -d . -1 -2 -5
Enter a password at the prompt and make the menu choices shown in Example 12-11.
Example 12-11. Create a signing certificate
Generating key. This may take a few moments... 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signning key 6 - CRL signning key Other to finish 0 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signning key 6 - CRL signning key Other to finish 5 0 - Digital Signature 1 - Non-repudiation 2 - Key encipherment 3 - Data encipherment 4 - Key agreement 5 - Cert signning key 6 - CRL signning key Other to finish 9 Is this a critical extension [y/n]? n Is this a CA certificate [y/n]? n Enter the path length constraint, enter to skip [<0 for unlimited path]: Is this a critical extension [y/n]? n 0 - SSL Client 1 - SSL Server 2 - S/MIME 3 - Object Signing 4 - Reserved for futuer use 5 - SSL CA 6 - S/MIME CA 7 - Object Signing CA Other to finish 3 0 - SSL Client 1 - SSL Server 2 - S/MIME 3 - Object Signing 4 - Reserved for futuer use 5 - SSL CA 6 - S/MIME CA 7 - Object Signing CA Other to finish 9 Is this a critical extension [y/n]? n
In Example 12-11, the certs.mozdev.org/signing certificate references the certs.mozdev.org CA certificate. The Digital Signature option is also set so a signed remote Mozilla application can be compiled from the certificate.
Export the signing certificate with the following command:
C:\NSS\bin>certutil -L -d . -n "certs.mozdev.org/signing" -a -o eric.cacert
You can send the resulting file to the person who requested the Signing Certificate. That person can then use it to create signed remote applications, as described later in this chapter in the Section 12.6.3.1 section.
12.5.5. Distributing Distribution Certificates
The distribution certificate (certs_mozdev.cacert in Example 12-10) must be installed into a user's Mozilla web browser before she can use signed applications that come from certificates you've distributed. It's best to state clearly on the web site hosting these signed remote Mozilla applications that the distribution certificate is needed. If you distribute many different signing certificates, and they are used by all signed remote Mozilla applications, then all applications can use that same distribution certificate (as they do with Verisign's and other certificate authorities).
To allow users to install the distribution certificate, create a link to the certificate file on your web page:
<a href="certs_mozdev.cacert">Install the MozDev CA Certificate</a>
Also make sure that your web server uses the MIME type application/x-x509-ca-cert for .cacert files.
If the web server is set up correctly, the user will get a dialog box that looks like Figure 12-5. Tell the user to select the options for “web sites” and “software developers.”
After the certificate is installed, it will appear in the Certificate Manager, as shown in Figure 12-6. The Certificate Manager can be accessed via the global Mozilla preferences (Edit > Preferences > Privacy & Security > Certificates). Mozilla is then ready to run signed remote Mozilla applications bearing signatures from your certificate authority.
12.6. Creating Signed Remote Applications
Security in Mozilla's web browser is designed to meet today's advanced scripting needs in a secure manner. Mozilla is a much more secure browser than past Netscape 4.x and Internet Explorer releases because it has a better sense of what remote scripts can and cannot do.
Because of Mozilla's approach toward potentially insecure applications, if you decide to serve up your own application remotely, remember that you will not have automatic access to the chrome in the way you do when you have a registered, locally installed Mozilla application. Unless you sign your application or have the user turn on a special preference (see Section 12.3), services like XPConnect will not be available.
In Mozilla, you can bundle any number of files into a JAR archive (which, you'll recall from Chapter 6, is just a zip file with a JAR suffix) and designate the archive as an object that can be signed. This designation makes it very easy to produce an entire signed and secure remote Mozilla application because it stores your application in a single file type that Mozilla already treats as a separate package.
This section provides an overview of the signed script technology and shows you how to create signed applications that live on the server but take full advantage of the user's local chrome, including Mozilla components.
12.6.1. certs.mozdev.org CA Certificate
Before users can load signed applications, a CA certificate must be installed into their installed copy of Mozilla. Once this certificate is installed in a browser, all MozDev-signed applications can work with this certificate. This setup makes things easier on users who access many of these signed applications because they do not have to install a new certificate for each one. Also, if the user wants to use applications from other certificate authorities, they need to install a distribution certificate from that certificate authority.
Installing the certificate is easy. Just provide the users with a regular link on a web page -- for example, http://certs.mozdev.org/certs_mozdev.cacert. When loading this page, a dialog box pops up and asks the user to install the certificate. See the Section 12.6.3.2 section later in this chapter for more information about this process.
12.6.2. Signing Certificates
As a Mozilla application developer, you can obtain a common MozDev signing certificate and release a signed application that puts your application on par with other signed MozDev applications. If you consider your application mission-critical, however, you should go to a trusted CA such as Verisign. Mozilla already supports the VeriSign Netscape Object Signing CA, and discriminating users may find it more acceptable. A few other CAs listed in Mozilla's Certificate Manager may support Netscape Object Signing, so researching these options further may be worthwhile.
To get a certs.mozdev.org/signing certificate, send email to cert-request@mozdev.org. In return, you will receive a .cacert file that will be used to sign your remote Mozilla application.
SignTool (part of the NSS tool sets) takes a directory of files, zips them up into a JAR archive (refer to the section Section 12.6.3.1 later in this chapter to see how to do this), and signs the archive using the certificate you specify.
SignTool comes with the latest release of NSS, Version 3.4.1. On http://certs.mozdev.org, limited functionality versions of NSS contain SignTool for Windows and Linux that you can use instead for the processes in this book.
Use CertUtil to set up a database for SignTool. Next, run some commands to set up the certificate environment:
C:\NSS\bin>certutil -N -d . C:\NSS\bin>certutil -A -n “certs.mozdev.org/signing" -t ",,C" -i eric.cacert -d .
The first command creates an empty cert7.db file where certificates can be stored. The second imports your Signing Certificate into the database. SignTool can use only certificates that reside in a database, which is the reason for this process.
12.6.3. Creating and Signing the Application
When someone obtains a private key (which is part of a Signing Certificate), they can encrypt their scripts and produce a public key. The relationship of the private key and the public key is called a private-public key pair. Using this relationship, you can create a signed Mozilla application and make it available to users in three steps:
- Build the application itself, including the XUL, CSS, JavaScript, and whatever else you use to create the Mozilla application.
For this section, let's assume that you already created the XUL and JavaScript for the application and have all the files and directories together.
- Archive and sign the application. SignTool takes care of both steps simultaneously, putting your application files in a JAR with a digital signature to validate everything.
The signing process described next in Section 12.6.3.1 deals entirely with SignTool.
- Distribute your application (see the later section Section 12.6.3.2).
12.6.3.1. Signing the application
Security is not simple. Security technologists and vendors work hard to make sure that evildoers cannot abuse their encryption schemes, keys, and other tricks. Tools like SignTool can hide some of this complexity from you. When you sign an application, you create a digital signature in the archive that is based on the relationship of the files being signed, as Figure 12-7 illustrates.
SignTool automates these steps for you, so you don't worry about them. However, knowing these processes and seeing how these transactions take place can be useful, especially since using signed applications with Mozilla doesn't always work as expected and long-term directions for signed applications in Mozilla are uncertain. This uncertainty makes long-term deployment of signed remote Mozilla applications a risky option.
To start off your remote Mozilla signed application development, you can do something as simple as place one XUL and one JavaScript file in a single directory. Then move it into a NSS bin directory such as C:\NSS\bin and issue the command:
C:\NSS\bin>signtool -d . -k"certs.mozdev.org/signing" -p"password_of_database" -Z"myapp.jar" myappfiles/
The -d option is where the certificate database resides and -k is the certificate name.
12.6.3.2. Distributing the application
Once the file is created from the -Z option (e.g., myapp.jar from the example above), you can put it online. On the application's web page, note that the application is signed and put a link to http://certs.mozdev.org/certs_mozdev.cacert so users can install the necessary MozDev certificate if they do not have it.
To access the application online, you must use a special URL format. This format looks like jar:http://certs.mozdev.org/myapp.jar!/myapp.xul and points into the JAR at the main application file. This URL is difficult to type, so it may be wise to link it for user access or set up a redirected URL to that address, as shown in Example 12-12.
Example 12-12. Sample redirect into a signed application
<HTML> <HEAD> <META HTTP-EQUIV="REFRESH” CONTENT="0; URL= jar:http://certs.mozdev.org/sample.jar!/sample.xul"> </HEAD> </HTML>
12.6.4. Receiving a Signed Application
As shown in Figure 12-8, when Mozilla receives a JAR, it must check it for validity before displaying the contents. A public key in certs_mozdev.cacert must be used along with the digital signature to make sure that the contents are not tampered with and that the signature is valid.
//FIXME did we loose content here?
When you are developing a signed remote Mozilla application, clear a JAR's cache before trying to reload an updated version of it. Clearing the cache can be done most easily by restarting Mozilla. If you or your users do not do clear it, the consumer of the application will probably wind up with a blank screen.
12.7. Expanded Privileges in Mozilla
While the security aspect of signed objects is nice, the ability to make remote JavaScript do just about anything is even better for web developers because it avoids the perceived complexity of languages like C++. Also, JavaScript, along with Perl and PHP, has always been a preferred language in the web environment.
Knowing that Internet Explorer no longer has a huge advantage when it comes to remote browser-based applications is also nice, since JavaScript and XPCOM in Mozilla provide a framework very similar to ActiveX. They also provide web page scripting in which you can create and use components from a web page or web application.
Table 12-1 shows the expanded privileges available to signed scripts. Signed applications are granted these privileges as a matter of course.
Privilege | Purpose |
UniversalBrowserRead | Reads sensitive browser data. This reading allows the script to pass the same origin check when reading from any document. |
UniversalBrowserWrite | Modifies sensitive browser data. This modification allows the script to pass the same origin check when writing to any document. |
UniversalXPConnect | Gives unrestricted access to browser APIs using XPConnect. |
UniversalPreferencesRead | Reads preferences using the navigator.preference method. |
UniversalPreferencesWrite | Allows you to set preferences using the navigator.preference method. |
CapabilityPreferencesAccess | Allows you to read/set the preferences that define security policies, including which privileges are granted and denied to scripts. (You also need UniversalPreferencesRead/Write.) |
UniversalFileRead | Handles window.open of file:// URLs. Makes the browser upload files from the user's hard drive by using <input type="file">. |
The JavaScript features require expanded privileges and the target used to access each feature. Unsigned scripts cannot do the following:
- Use an about: format URL other than about:blank; requires UniversalBrowserRead.
- Use the history object to find out what other sites the user visited or how many other sites the user visited in this session. Doing so requires UniversalBrowserRead.
- When using navigator object, get the preference value by using the preference method. Getting such a value requires UniversalPreferencesRead.
- Set the preference value using the preference method; getting this value requires UniversalPreferencesWrite.
- Add or remove the directory bar, location bar, menu bar, personal bar, scroll bar, status bar, or toolbar. These are done using the window object and require UniversalBrowserWrite.
- Use the methods and properties in the Table 12-2 under the indicated circumstances.
Method / property | Description |
EnableExternalCapture | Captures events in pages loaded from different servers. Follow this method with captureEvents. |
Close | Unconditionally closes a browser window. |
moveBy, moveTo | Moves a window off of the screen. |
Open |
|
resizeTo, resizeBy | Resizes a window smaller than 100 x 100 pixels or larger than the screen can accommodate. |
innerWidth, innerHeight | Sets the inner width of a window to a size smaller than 100 x 100 or larger than the screen can accommodate. |
This snippet of code shows how to use the privilege manager in JavaScript:
netscape.security.PrivilegeManager. enablePrivilege("UniversalBrowserWrite"); window.titlebar=no;
You can pass any privilege listed in Table 12-1 to the enablePrivilege method, which is accessed through the netscape.security.PrivilegeManager object. This object is recognized globally. In this example, the code hides the titlebar via the window object.
Security is extremely important, so it is important that some means of granting special privileges to trusted scripts for accessing Mozilla components be available. In essence, signed scripts are Mozilla's version of ActiveX.
The parallels become even more apparent when you consider access to XPConnect as one of the security model's main boundaries. Just as ActiveX makes COM available in IE, signing makes XPCOM available in remote Mozilla applications. Given all that is possible in XPCOM, this chapter leaves what can be archived with remote Mozilla applications and XPConnect up to your imagination.
12.8. Signed Remote Snake Game
In this section, we look at an enhanced version of the Snake game presented earlier in the chapter. The enhanced version uses XPConnect to provide a total full-screen display of the game on the Windows platform as a remote application.
12.8.1. How to Expand Mozilla to Full Screen
The best way to expand Mozilla to a full screen mode is through full-screen functions provided in an instance of navigator.xul. These functions run in the Windows build of Mozilla via the Full Screen item in the View menu. These functions also work in Linux and Mac, but do not provide 100% full-screen mode, as some menus and titlebars still show.
The problem here is the current window's navigator.xul document, which needs to be accessed to get these full-screen functions. A document loaded in that window just can't use something like window.parent to get to it, so another route must be found.
This route runs through the nsIWindowMediator interface by the way of XPConnect. It gives access to the current browser window's navigator.xul document's window object. Example 12-13 includes the code for this window access process, along with the functions used to create the full-screen effect.
Example 12-13. Function for switching screen modes
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); const MEDIATOR_CONTRACTID="@mozilla.org/appshell/window-mediator;1"; const nsIWindowMediator=Components.interfaces.nsIWindowMediator; var windowManager= Components.classes[MEDIATOR_CONTRACTID].getService(nsIWindowMediator); var hideSidebar=true; var isRegular=true; function switchScreen( ) {
if(isRegular) { try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); mainWindow = windowManager.getMostRecentWindow("navigator:browser"); } catch(e) { alert(e); } if(mainWindow.sidebar_is_hidden( )) hideSidebar=false; if(hideSidebar) mainWindow.SidebarShowHide( ); mainWindow.BrowserFullScreen( ); window.fullScreen=true; window.locationbar.visible=false; window.toolbar.visible=false; isRegular=false; } else { try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); mainWindow = windowManager.getMostRecentWindow("navigator:browser"); } catch(e) { alert(e); } window.locationbar.visible=true; window.toolbar.visible=true; if(hideSidebar) mainWindow.SidebarShowHide( ); mainWindow.BrowserFullScreen( ); isRegular=true; } }
windowManager, which is spawned by XPConnect, creates the mainWindow variable. By using the getMostRecentWindow function for navigator:browser, the Mozilla application window you currently use becomes available. Next, tests are made in code for the window status determine if it is regular or full screen. Appropriate action can then be made by calling the SidebarShowHide function.
As you can see in Example 12-13, code for hiding the toolbar and location bar is also present. This code is accomplished not by the mainWindow created through XPConnect, but by the existing window object:
window.locationbar.visible=false; window.toolbar.visible=false;
Using both the mainWindow and window objects allows the creation of a full-screen remote Mozilla application by allowing XPConnect privileges. Figure 12-9 shows the result on Windows -- a total full screen for a signed remote Mozilla game!
12.9. Mozilla's XML Extras and SOAP
Mozilla has built functions called XML Extras that allow the use of XML as data in both JavaScript and C++. Such functions are an XML Serializer, XMLHttpRequest, XML Parser, SOAP-based RPC, and XML Persistence. You can find more information about these functions, along with examples, at http://www.mozilla.org/xmlextras/.
The following sections assume that you are familiar with SOAP and .NET. If not, some good O'Reilly books available on these subjects can help get you started.
12.9.1. Mozilla, SOAP, and .NET
In this section, SOAP is used to access data in a .NET web service, therefore allowing the Snake game to have features such as a saved game score, a retrieved game score, and a list of high scores.
As of Mozilla 1.0, the SOAP functions of Mozilla do not work in signed scripts. This bug will be corrected in the future. All JavaScript using SOAP functions in this section is loaded externally of the signed JAR. These SOAP functions do not require enhanced privileges.
12.9.2. Setting Up a .NET Web Service
The easiest way to create a .NET web service is through Visual Studio.NET, which provides a template for creating these services. Example 12-14 shows a bare minimum of C# code used to compile the functions that return a value to the Snake game.
Obviously, a full implementation would need a database to store these scores. For this section, seeing how the interfaces work for these SOAP functions is more important.
Example 12-14. Minimal .NET web service
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
namespace SnakeService
{
[WebServiceAttribute (Namespace="uri:SnakeScore")]
public class SnakeService : System.Web.Services.WebService
{
public SnakeService( )
{
InitializeComponent( );
}
#region Component Designer generated code
private IContainer components = null;
private void InitializeComponent( ){}
protected override void Dispose( bool disposing )
{
if(disposing && components != null)
{
components.Dispose( );
}
base.Dispose(disposing);
}
#endregion
[WebMethod]
public string SaveScore(string PlayerName, string Score)
{
return “Save of score successful.";
}
[WebMethod]
public string GetScore(string PlayerName)
{
int Score = 990;
return Score.ToString( );
}
[WebMethod]
public string GetHighScores( )
{
return “EDM 1000,SLK 200,BRP 10";
}
}
}
The most important part of Example 12-14 is the WebServiceAttribute because it sets up a URI reference to the SnakeService object. When a request is sent from the Snake game to the .NET SnakeService, uri:SnakeScore becomes the name for the object providing functions for getting and setting the game's score.
In Example 12-14, all the parameter and return values are of the string type. Considering the brevity of this example, expanding it would not be hard. Functions using other objects and database connections would really make it a true web application.
12.9.3. .NET WSDL
.NET automatically generates WSDL interfaces inside a web service. Mozilla SOAP doesn't need to reference a WDSL file to make SOAP transactions.
Example 12-15 is a portion of the WDSL that .NET generates and is the specific portion that relates directly to sending raw SOAP calls to the SnakeService. Also, only the definitions for the GetScore function are in this abbreviated definition.
Example 12-15. Abbreviated WSDL as produced by .NET web service.
<?xml version="1.0" encoding="utf-8"?>
<definitions
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="uri:SnakeScore"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
targetNamespace="uri:SnakeScore"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<s:schema elementFormDefault="qualified"
targetNamespace="uri:SnakeScore">
<s:element name="GetScore">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="PlayerName"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetScoreResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="GetScoreResult"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</types>
<message name="GetScoreSoapIn">
<part name="parameters" element="s0:GetScore" />
</message>
<message name="GetScoreSoapOut">
<part name="parameters" element="s0:GetScoreResponse" />
</message>
<portType name="SnakeServiceSoap">
<operation name="GetScore">
<input message="s0:GetScoreSoapIn" />
<output message="s0:GetScoreSoapOut" />
</operation>
</portType>
<binding name="SnakeServiceSoap" type="s0:SnakeServiceSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="GetScore">
<soap:operation soapAction="uri:SnakeScore/GetScore"
style="document" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="SnakeService">
<port name="SnakeServiceSoap" binding="s0:SnakeServiceSoap"> <soap:address location="http://localhost/SnakeService/SnakeService.asmx" /> </port> </service> </definitions>
The most important thing to notice in this WSDL is the soapAction. In Example 12-15, uri:SnakeScore/GetScore is defined as the identifier for the SnakeScore object's GetScore function. This identifier makes the call to this function in Example 12-19.
12.9.4. SOAP Call XML Formats
When .NET and Mozilla serialize SOAP calls, they produce different XML formats. The namespace prefixes differ, and Mozilla produces more of these namespaces in its version of the SOAP message. However, the code comparison in Examples 12-16 and 12-17 mean fundamentally the same thing. Thus, .NET and Mozilla are able to communicate.
Example 12-16. XML format for SOAP calls of Mozilla
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xs="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"> <env:Header/> <env:Body> <a0:SaveScore xmlns:a0="uri:SnakeScore"> <PlayerName xsi:type="xs:string">EDM</PlayerName> <Score xsi:type="xs:string">10</Score> </a0:SaveScore> </env:Body> </env:Envelope>
Example 12-17. .NET format for SOAP calls of Mozilla
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <SaveScore xmlns="uri:SnakeScore"> <PlayerName>EDM</PlayerName> <Score>10</Score> </SaveScore> </soap:Body> </soap:Envelope>
Realizing these formatting differences in Examples 12-16 and 12-17 is important because if you develop with SOAP by using .NET and Mozilla, you are bound to run across variations in your future software projects. Luckily the W3C has set a standard that Mozilla and Microsoft adheres to.
12.9.5. Adding SnakeService SOAP to Snake
Developers use built-in methods to just write JavaScript and use SOAP easily. There is no need for enhanced privileges because nothing could affect the client adversely. However, there are some limitations on how SOAP JavaScript is used.
Mozilla sets one level of security with the SOAP function by requiring that the web service and the JavaScript file that makes the SOAP functions access that service be on the same domain. For example, you will not encounter problems when running everything as localhost (as shown in the examples). If you try to move the JavaScript files to mozdev.org, though, they will no longer work.
Another limitation of the SOAP functions is that they can't be used directly in XUL documents. However, a hack, discussed in the next section, can get around this limitation.
12.9.6. Make SOAP Functions Work in XUL Documents
The best way to circumvent the SOAP-in-XUL-documents problem in Mozilla 1.0 (and probably 1.1) is to initially load the JavaScript file containing the SOAP functions from an HTML file, as shown in Example 12-18.
Example 12-18. Preloading scores.js into cache with an HTML association
<html> <head> <script src="http://localhost/nss/bin/scores.js"></script> </head> <body> <script> window.location.href="jar:http://localhost/nss/bin/snake.jar!/scores.xul"; </script> </body> </html>
As stated earlier, scores.js (containing the SOAP functions) must exist outside the JAR file. It is loaded up into cache with this HTML document, and then the page redirects to the XUL file that has the SOAP function user interface. That JavaScript file is already loaded up in cache and will work fine.
Remember that doing this is a hack, but later versions of Mozilla that fix the SOAP-in-XUL-document problem would still not break this code.
12.9.7. Examining SOAP Functions for Snake
Example 12-19 shows how to create two functions (SaveScore and SaveScoreResponse) to handle SOAP transactions with the previously examined .NET web service.
Example 12-19. SaveScore SOAP function
const soapVersion = 0; // Version 1.1 const object = "uri:SnakeScore"; const transportURI = "http://localhost/SnakeService/SnakeService.asmx"; // SAVE PLAYER SCORE function SaveScore( ) { var Score = window.opener.document.getElementById("currentscore").getAttribute("value"); var PlayerName = document.getElementById("saveInitials").value; var method = "SaveScore"; var headers = new Array( ); var params = new Array(new SOAPParameter(PlayerName,"PlayerName"), new SOAPParameter(Score,"Score")); var call = new SOAPCall( ); call.transportURI = transportURI; call.actionURI = object+"/"+method; call.encode(soapVersion,method,object,headers.length,headers,params.length,params); var currentRequest = call.asyncInvoke(SaveScoreResponse); } function SaveScoreResponse(resp,call,status) { // Display confirmation // Part of content of SOAP message returned alert(resp.body.firstChild.firstChild.firstChild.data); }
The object defined here is the same as the namespace defined in Example 12-14. Again, this snake score object (uri:SnakeScore) is simply an identifier to that exact web service. The transportURI is the location of the web service. As you can see here, it runs localhost along with the files for the Snake remote Mozilla application. Moving into the actual SaveScore function, a PlayerName is pulled from a <textbox> in the XUL.
The method is the name of the function in the .NET web service with which this code will communicate. headers is an empty array because no SOAP headers are needed for this simple example. Two SOAPParameters are also defined here in an array, and they are just simple strings. Moving on down the code in Example 12-19, a new SOAPCall( ) is defined into the call variable. Two URIs are set up for this SOAPCall object: call.transportURI and call.actionURI, which combines an object and a method into one string. The next two lines, encode and asyncInvoke the SOAPCall and the encoded XML message, as shown in Examples Example 12-16 and Example 12-17, are sent to the .NET web service. When a response is received, the SaveScoreResponse function is called.
Currently, a hack is used in SaveScoreResponse so a DOM property accesses the XML of the returned SOAP message. This implementation is not the best way, but the optimal way doesn't currently work. Here is an easy version of the code:
ret = resp.getParameters(false, new Array( )); alert(ret[0]);
If you put this code in SaveScoreResponse and it works, it could replace the code in Examples 12-16 and 12-17. You need to play around with these different SOAP functions and see what works for you. Again, the code just shown does not work in Mozilla 1.0, but will hopefully work in all future versions of Mozilla.
Example 12-20 shows the code for GetResponse and GetHighScores. Compare the JavaScript code to the code and WSDL in Examples 12-14 and 12-15 to see how they all work together.
Example 12-20. Code for GetScore and GetHighScores
// GET PLAYER SCORE var ScoreElement; // Make this accessible to GetScoreResponse function GetScore( ) { ScoreElement = window.opener.document.getElementById("currentscore"); var PlayerName = document.getElementById("getInitials").value; var method = "GetScore"; var headers = new Array( ); var params = new Array(new SOAPParameter(PlayerName,"PlayerName")); var call = new SOAPCall( ); call.transportURI = transportURI; call.actionURI = object+"/"+method; call.encode(soapVersion,method,object,headers.length,headers,params.length,params); var currentRequest = call.asyncInvoke(GetScoreResponse); } function GetScoreResponse(resp,call,status) { ScoreElement.setAttribute("value",resp.body.firstChild.firstChild.firstChild.data); alert("Your score has been reinstated. You can now return to the game."); } // GET HIGH SCORES function GetHighScores( ) { var method = "GetHighScores"; var headers = new Array( ); var params = new Array( ); var call = new SOAPCall( ); call.transportURI = transportURI; call.actionURI = object+"/"+method; call.encode(soapVersion,method,object,headers.length,headers,params.length,params); var currentRequest = call.asyncInvoke(GetHighScoresResponse); } function GetHighScoresResponse(resp,call,status) { alert(resp.body.firstChild.firstChild.firstChild.data); }
Figure 12-10 shows how the XUL interface to these functions in Example 12-20 is designed. Here the score is replaced with “990,” as this number is pulled from the code shown in Example 12-14.
12.10. Looking Forward
This chapter focuses on just one of many new trends outside of the original project mandate that emerged in the Mozilla developer community. Now that Mozilla 1.0 is released, its future direction will be shaped by the community itself, and Mozilla will become whatever the community would like it to be.
Remote applications are definitely one area of Mozilla development that will get more attention as Mozilla matures. Other areas that will probably also be noticed include development tools (some existing development tools are discussed in Appendix B), embedding, SVG support, and XSLT support.
Remember that Mozilla is open to new ideas and is always looking for contributions. If you can think of a way to improve Mozilla, or if you think of something that should be added, become a part of the community and help expand the possibilities of Mozilla and all Mozilla applications.
Get Creating Applications with Mozilla now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.