SOAP is not part of the default build. git-svn-id: svn://10.0.0.236/trunk@114757 18797224-902f-48f8-a5cc-f745e15eee43
1099 lines
47 KiB
HTML
1099 lines
47 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
<html>
|
|
<head>
|
|
|
|
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
|
|
<title>Soap Scripts in Mozilla</title>
|
|
</head>
|
|
<body>
|
|
|
|
<div align="Center">
|
|
<h1>SOAP Scripts in Mozilla</h1>
|
|
<font color="#999999">February 16, 2002</font><br>
|
|
|
|
<h3><font color="#999999">Ray Whitmer</font></h3>
|
|
|
|
<h2><font color="#666666">Abstract</font></h2>
|
|
|
|
<div align="Left"><font color="#000000">Microsoft and others have advocated
|
|
SOAP as a way to encode and exchange public data structures between agents
|
|
on the web. The browser client is the most universal web agent in
|
|
existence, and Javascript is the standard, interoperable way of scripting
|
|
browsers. Scriptable SOAP in browsers gives clients and servers more
|
|
to say to each other through existing http-xml services, providing scripts
|
|
with persistence, database, and access to other web services not tied to
|
|
the request and response cycles of the HTML-based user interface.
|
|
Web data structures, exchanged in a platform-neutral way, should become
|
|
as fundamental to web agents as web content is today. The key to this
|
|
is a tight binding to the natural data of Javascript so that the script
|
|
can simply use the data instead of tediously encoding and extracting the
|
|
data from the XML.</font><br>
|
|
|
|
<h2>SOAP Services</h2>
|
|
There are a number of sources for services available on the web being
|
|
set up, such as XMethods. Apache supports modules for SOAP that this author
|
|
has used to author services for purposes of testing and entertainment.
|
|
Once it is set up, it is as simple as writing a function in Javascript,
|
|
Java, or any other of a number of supported languages and then writing a
|
|
simple service description in XML or submitting a form to deploy the service.
|
|
There are toolkits available from Microsoft and other webserver providers
|
|
for authoring such services as well.<br>
|
|
|
|
<h2>SOAP Blocks</h2>
|
|
|
|
<h3>Parameters<br>
|
|
</h3>
|
|
SOAP-based services exchange message envelopes which contain blocks of
|
|
XML data roughly corresponding to the parameters of a service call.
|
|
When an rpc-style message is exchanged, blocks representing the regular
|
|
parameter blocks are placed inside an element which identifies the object
|
|
and method being invoked, which is placed inside the body. In a non-RPC
|
|
message, the blocks are placed directly inside the body instead of under
|
|
the method element. <br>
|
|
|
|
<h3>Header Blocks<br>
|
|
</h3>
|
|
If there are blocks which are optional or independently added or processed,
|
|
these are carried in the header with an assigned role and marked if the recipient
|
|
is required to understand them.<br>
|
|
|
|
<h3>Encodings<br>
|
|
</h3>
|
|
Interpretation of each block depends upon the encoding that was used,
|
|
which is clearly specified in the message. If the standard SOAP encoding
|
|
is used, then XML Schema types control the interpretation the data within
|
|
each block.<br>
|
|
|
|
<h2>Using the Low-Level SOAP API</h2>
|
|
To use the low-level API, the user creates a SOAPCall object, encodes
|
|
the function call with a list of headers and regular parameters, and invokes
|
|
the call, which returns a response which contains the results of the service
|
|
call including a fault generated by the service which processed the message,
|
|
output parameters, and/or header blocks. If the call is invoked asynchronously,
|
|
then a function is given which receives the response when it arrives from
|
|
the remote service.<br>
|
|
<br>
|
|
Besides Javascript, the below-described operations should also generally
|
|
work for other xpconnect-supported languages in Mozilla such as C++/XPCOM
|
|
and Python, because language-independent cross-platform interfaces were
|
|
used.<br>
|
|
|
|
<h3>Conventions<br>
|
|
</h3>
|
|
Names or short description in angle brackets represent values or objects
|
|
of the named type which may be literal, constructed via "new", or a variable.
|
|
Occasionally the same syntax with a longer description represents script
|
|
performing an operation. Quoted angle brackets indicate a string of
|
|
the described form.<br>
|
|
<br>
|
|
So the following operation:<br>
|
|
|
|
<ul>
|
|
<li><font color="#993300"><i><Foo></i></font> = new Foo(<font color="#993300"><i>
|
|
<FooFact></i></font> );</li>
|
|
<li><font color="#993300"><i><Foo></i></font>.bar = <font color="#993300"><i>
|
|
<any value or object></i></font>;</li>
|
|
<li><font color="#993300"><i><Baz></i></font> = <font color="#993300"><i>
|
|
<Foo></i></font>.find("<font color="#993300"><i><name></i></font>
|
|
");</li>
|
|
<li><font color="#993300"><i><find out what you have></i></font></li>
|
|
|
|
</ul>
|
|
might be replaced in a real Javascript program with<br>
|
|
<br>
|
|
<code>var myFoo = new Foo(new FooFact("always","Great","Baz"));<br>
|
|
myFoo.bar = 3.14159265;<br>
|
|
var myGreatBaz = myFoo.find("Great");<br>
|
|
if (checkBaz(myGreatBaz)) {<br>
|
|
alert(myGreatBaz.description);<br>
|
|
}<br>
|
|
</code><br>
|
|
where "myFoo" is a variable holding an object of type Foo and myGreatBaz
|
|
is a variable holding an object of type Baz.<br>
|
|
|
|
<h3>Basic Operations<br>
|
|
</h3>
|
|
For basic SOAP operations is all most users need to do.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Basic Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Create a parameter block, setting the Javascript
|
|
value and name for rpc-style call<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPParameter></i></font>
|
|
= new SOAPParameter(<font color="#993300"><i><any value or object></i></font>
|
|
, "<font color="#993300"><i><name></i></font>");</li>
|
|
<li> // or </li>
|
|
<li><font color="#993300"><i><SOAPParameter></i></font>
|
|
= new SOAPParameter();</li>
|
|
<li><font color="#993300"><i><SOAPParameter></i></font>.value
|
|
= <font color="#993300"><i><any value or object></i></font> ;</li>
|
|
<li> <font color="#993300"><i><SOAPParameter></i></font>
|
|
.name = "<font color="#993300"><i><name></i></font>";</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Set parameters in a Javascript array<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPParameter array></i></font>
|
|
= new Array(<font color="#993300"><i><SOAPPerameter></i></font> [,...]);</li>
|
|
<li> // or</li>
|
|
<li><font color="#993300"><i><SOAPParameter array></i></font>
|
|
= new Array();</li>
|
|
<li><font color="#993300"><i><SOAPParameter array></i></font>
|
|
[0] = <font color="#993300"><i><SOAPParameter></i></font> ;</li>
|
|
<li>[...]</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Create and encode the parameters in a basic SOAP
|
|
1.1 rpc-style message<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPCall></i></font> = new
|
|
SOAPCall();</li>
|
|
<li><font color="#993300"><i><SOAPCall></i></font>.transportURI
|
|
= "<font color="#993300"><i><http-based service URI></i></font>"</li>
|
|
<li><font color="#993300"><i><SOAPCall></i></font>.encode(0,
|
|
"<font color="#993300"><i><method name></i></font> ", "<font color="#993300"><i>
|
|
<service namespace></i></font>", 0, null, <font color="#993300"><i>
|
|
<SOAPParameter array></i></font> .length, <font color="#993300"><i>
|
|
<SOAPParameter array></i></font>);</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Invoke call (send call message and receive
|
|
response message)<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPResponse></i></font> =
|
|
<font color="#993300"><i><SOAPCall></i></font>.invoke();</li>
|
|
<li><font color="#993300"><i><process the response -- see below></i></font></li>
|
|
<li> // or</li>
|
|
<li> <font color="#993300"><i><SOAPCall></i></font>.asyncInvoke(<font color="#993300"><i>
|
|
<SOAPResponseListener></i></font> );</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Handle completion of async SOAP call.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li>function <font color="#993300"><i><SOAPResponseListener
|
|
name></i></font>(<font color="#993300"><i><SOAPResponse></i></font>
|
|
, <font color="#993300"><i><SOAPCall></i></font>, <font color="#993300"><i>
|
|
<error></i></font>)</li>
|
|
<li>{</li>
|
|
<li> if (error != 0) {</li>
|
|
<li> <font color="#993300"><i><action to
|
|
be taken on failure to transport message></i></font></li>
|
|
<li> }</li>
|
|
<li> <font color="#993300"><i><process the response --
|
|
see below></i></font></li>
|
|
<li>}</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get service's failure, if any.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPFault></i></font> = <font color="#993300"><i>
|
|
<SOAPResponse></i></font>.fault;</li>
|
|
<li>if (<font color="#993300"><i><SOAPFault></i></font>
|
|
!= null) {</li>
|
|
<li> <font color="#993300"><i><namespace URI string></i></font>
|
|
= <font color="#993300"><i><SOAPFault></i></font> .faultNamespace;</li>
|
|
<li> <font color="#993300"><i><name string></i></font>
|
|
= <font color="#993300"><i><SOAPFault></i></font> .faultCode;</li>
|
|
<li> <font color="#993300"><i><summary string></i></font>
|
|
= <font color="#993300"><i><SOAPFault></i></font> .faultString;</li>
|
|
<li> <font color="#993300"><i><actor URI string></i></font>
|
|
= <font color="#993300"><i><SOAPFault></i></font> .actorURI;</li>
|
|
<li> <font color="#993300"><i><action to be taken in
|
|
case of fault></i></font></li>
|
|
<li>}</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get returned parameters from rpc-style response<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPParameter array></i></font>
|
|
= <font color="#993300"><i><SOAPResponse></i></font> .getParameters(true,
|
|
{});</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Process Javascript values, etc. of returned parameters<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li>for (i = 0; i != <font color="#993300"><i><SOAPParameter
|
|
array></i></font>.length; i++)</li>
|
|
<li>{</li>
|
|
<li> <font color="#993300"><i><SOAPParameter></i></font>
|
|
= <font color="#993300"><i><SOAPParameter array></i></font>[i];</li>
|
|
<li> <font color="#993300"><i><value or object></i></font>
|
|
= <font color="#993300"><i><SOAPParameter></i></font> .value;</li>
|
|
<li> <font color="#993300"><i><name string></i></font>
|
|
= <font color="#993300"><i><SOAPParameter></i></font> .name;</li>
|
|
<li> <font color="#993300"><i><checking and processing
|
|
of result></i></font></li>
|
|
<li>}</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3></h3>
|
|
The above operations are what every user of the lowlevel SOAP toolkit
|
|
needs to invoke service requests and interpret the responses, as is easily
|
|
seen by looking at some of the samples. The bulk of the operations
|
|
that follow will generally be used much less frequently, but they need to
|
|
be there for cases where they are needed. The casual reader may
|
|
skip the remaining tables of operations in this section, or scan for features
|
|
of interest.<br>
|
|
<h3>Header Operations</h3>
|
|
For additional information, the user can send or receive header blockss.
|
|
Sending and receiving header blocks is not very different from sending
|
|
and receiving parameters as described above.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Header Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Create a Header Block<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
= new SOAPHeaderBlock(<font color="#993300"><i><any value or object></i></font>
|
|
, "<font color="#993300"><i><name></i></font>", "<font color="#993300"><i>
|
|
<namespaceURI></i></font> ");</li>
|
|
<li> // or </li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
= new SOAPHeaderBlock();</li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
.value = <font color="#993300"><i><any value or object></i></font>;</li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
.name = "<font color="#993300"><i><name></i></font> ";</li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
.namespaceURI = "<font color="#993300"><i><namespaceURI></i></font>
|
|
";</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Establish non-default role of a header block<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
.actorURI = "<font color="#993300"><i><actorURI></i></font> ";</li>
|
|
<li><font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
.mustUnderstand = <font color="#993300"><i><true or false></i></font>
|
|
;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Set header blocks in a Javascript array<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPHeaderBlock array></i></font>
|
|
= new Array(<font color="#993300"><i><SOAPHeaderBlock></i></font> [,...]);</li>
|
|
<li> // or</li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock array></i></font>
|
|
= new Array();</li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock array></i></font>
|
|
[0] = <font color="#993300"><i><SOAPHeaderBlock></i></font> ;</li>
|
|
<li> [...]</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Encode the headers in a SOAP 1.1 rpc-style message<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPCall></i></font>.encode(0,
|
|
"<font color="#993300"><i><method name></i></font> ", "<font color="#993300"><i>
|
|
<service namespace></i></font>", <font color="#993300"><i><SOAPHeaderBlock
|
|
array></i></font> .length, <font color="#993300"><i><SOAPHeaderBlock
|
|
array></i></font>, <font color="#993300"><i><SOAPParameter array></i></font>
|
|
.length, <font color="#993300"><i><SOAPParameter array></i></font>
|
|
);</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get returned headers<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPHeaderBlock array></i></font>
|
|
= <font color="#993300"><i><SOAPResponse></i></font> .getHeaderBlocks(true,
|
|
{});</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Process Javascript values, etc. of returned headers<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li>for (i = 0; i != <font color="#993300"><i><SOAPHeaderBlock
|
|
array></i></font>.length; i++)</li>
|
|
<li>{</li>
|
|
<li> <font color="#993300"><i><SOAPHeaderBlock></i></font>
|
|
= <font color="#993300"><i><SOAPHeaderBlock array></i></font>[i];</li>
|
|
<li> <font color="#993300"><i><value or object></i></font>
|
|
= <font color="#993300"><i><SOAPHeaderBlock></i></font> .value;</li>
|
|
<li> <font color="#993300"><i><name string></i></font>
|
|
= <font color="#993300"><i><SOAPHeaderBlock></i></font> .name;</li>
|
|
<li> <font color="#993300"><i><namespace URI string></i></font>
|
|
= <font color="#993300"><i><SOAPHeaderBlock></i></font> .namespaceURI;</li>
|
|
<li> <font color="#993300"><i><actor URI string></i></font>
|
|
= <font color="#993300"><i><SOAPHeaderBlock></i></font> .actorURI;</li>
|
|
<li> <font color="#993300"><i><must understand boolean></i></font>
|
|
= <font color="#993300"><i><SOAPHeaderBlock></i></font> .mustUnderstand;<br>
|
|
</li>
|
|
<li> <font color="#993300"><i><checking and processing
|
|
of result></i></font></li>
|
|
<li>}</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>Non-RPC Operations</h3>
|
|
For messages that are not intended to model RPC calls, there is no method
|
|
name or target object URI, and the parameters generally have namespaceURIs.
|
|
Otherwise, the basic operations are the same.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Non-RPC Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Setting the namespaceURI of a non-RPC parameter<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPParameter></i></font>
|
|
= new SOAPHeaderBlock(<font color="#993300"><i><any value or object></i></font>
|
|
, "<font color="#993300"><i><name></i></font>", "<font color="#993300"><i>
|
|
<namespaceURI></i></font> ");</li>
|
|
<li> // or </li>
|
|
<li><font color="#993300"><i><SOAPParameter></i></font>.namespaceURI
|
|
= "<font color="#993300"><i><namespaceURI></i></font> ";</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Encode a SOAP 1.1 non-rpc-style message<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPCall></i></font>.encode(0,
|
|
"", "", <font color="#993300"><i><header block array></i></font>.length,
|
|
<font color="#993300"><i><header block array></i></font>, <font color="#993300"><i>
|
|
<parameter array></i></font>.length, <font color="#993300"><i><parameter
|
|
array></i></font>)</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get returned parameters from non-rpc-style response<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPParameter array></i></font>
|
|
= <font color="#993300"><i><SOAPResponse></i></font> .getParameters(false,
|
|
{});</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>SOAP Supertypes</h3>
|
|
Header blocks and rpc-style and non-rpc-style parameters are similar. In
|
|
the following sections, these will usually be referred to collectively as
|
|
SOAPBlock objects..<br>
|
|
<br>
|
|
SOAPCall and SOAPResponse objects are also fairly similar as messages consisting
|
|
of blocks. In the following sections, calls and responses will usually
|
|
be referred to collectively as SOAPMessage objects.<br>
|
|
|
|
<h3>More Operations</h3>
|
|
The following table contains less-common operations.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Set or get an actionURI carried for the message
|
|
in the HTTP header<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPMessage></i></font>.actionURI
|
|
= "<font color="#993300"><i><action URI></i></font> ";</li>
|
|
<li>// or<br>
|
|
</li>
|
|
<li><font color="#993300"><i><action URI string></i></font>
|
|
= <font color="#993300"><i><SOAPMessage></i></font> .actionURI;<br>
|
|
</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Directly set the DOM element to represent the block's
|
|
encoded content, bypassing encoding of the value on the block<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPBlock></i></font>.element
|
|
= <font color="#993300"><i><DOM Element></i></font> ;<br>
|
|
</li>
|
|
|
|
</ul>
|
|
<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Directly get the DOM element that represents the
|
|
block's encoded content, bypassing decoding of the value on the block<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><DOM Element></i></font> =
|
|
<font color="#993300"><i><SOAPBlock></i></font>.element;<br>
|
|
</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Directly get the DOM element containing the SOAP
|
|
envelope , header, or body of an encoded message<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><DOM Element></i></font> =
|
|
<font color="#993300"><i><SOAPMessage></i></font>.envelope;</li>
|
|
<li>// OR</li>
|
|
<li><font color="#993300"><i><DOM Element></i></font> =
|
|
<font color="#993300"><i><SOAPMessage></i></font>.header;</li>
|
|
<li>// or</li>
|
|
<li><font color="#993300"><i><DOM Element></i></font> =
|
|
<font color="#993300"><i><SOAPMessage></i></font>.body;<br>
|
|
</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Directly set the DOM document to represent the
|
|
message's entire encoded content, bypassing encoding.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPMessage></i></font>.message
|
|
= <font color="#993300"><i><DOM Document></i></font> ;<br>
|
|
</li>
|
|
|
|
</ul>
|
|
<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Directly get the DOM document of an encoded message,
|
|
bypassing encoding.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><DOM Document></i></font> =
|
|
<font color="#993300"><i><SOAPMessage></i></font>.message;<br>
|
|
</li>
|
|
|
|
</ul>
|
|
<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get the method name and object URI, if any, of
|
|
the message.<br>
|
|
</td>
|
|
<td valign="Top"><font color="#993300"><i><method name string></i></font>
|
|
= <font color="#993300"><i><SOAPMessage></i></font> .method;<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get the actual SOAP version of an encoded message
|
|
-- 0 for SOAP 1.1 and 1 for SOAP 1.2.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><version integer></i></font>
|
|
= <font color="#993300"><i><SOAPMessage></i></font> .version;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Encode a SOAP 1.2 message.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPCall></i></font>.encode(1,
|
|
"<font color="#993300"><i><method name></i></font> ", "<font color="#993300"><i>
|
|
<service namespace></i></font>", <font color="#993300"><i><SOAPHeaderBlock
|
|
array></i></font> .length, <font color="#993300"><i><SOAPHeaderBlock
|
|
array></i></font>, <font color="#993300"><i><SOAPParameter array></i></font>
|
|
.length, <font color="#993300"><i><SOAPParameter array></i></font>
|
|
);</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Abort an in-progress async call -- this does not
|
|
necessarily cause the message not to be processed, but the API stops listening
|
|
for it to complete.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPCallCompletion></i></font>
|
|
= <font color="#993300"><i><SOAPCall></i></font> .asyncInvoke(<font color="#993300"><i>
|
|
<SOAPResponseListener></i></font>);</li>
|
|
<li>[...]</li>
|
|
<li><font color="#993300"><i><SOAPCallCompletion></i></font>
|
|
.abort();<br>
|
|
</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get the encoding (style) used to encode or decode
|
|
message. Not available on an unencoded call unless explicitly set --
|
|
use following operation instead.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font> =
|
|
<font color="#993300"><i><SOAPMessage></i></font>.encoding;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Set the encoding style (and associated styles)
|
|
used to encode a message.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font> =
|
|
new SOAPEncoding();</li>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font> =
|
|
<font color="#993300"><i><SOAPEncoding></i></font>.getAssociatedEncoding("<font color="#993300"><i>
|
|
<style URI></i></font>",<font color="#993300"><i><true to create></i></font>
|
|
);</li>
|
|
<li>[...]<font color="#993300"><i><customize encodings></i></font><br>
|
|
</li>
|
|
<li><font color="#993300"><i><SOAPMessage></i></font>.encoding
|
|
= <font color="#993300"><i><SOAPEncoding></i></font> ;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Specify the specific style used to encode or decode
|
|
specific blocks<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font> =
|
|
<font color="#993300"><i><SOAPEncoding></i></font>.getAssociatedEncoding("<font color="#993300"><i>
|
|
<style URI></i></font>",<font color="#993300"><i><true to create></i></font>
|
|
);</li>
|
|
<li><font color="#993300"><i><SOAPBlock></i></font>.encoding
|
|
= <font color="#993300"><i><SOAPEncoding></i></font> ;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3></h3>
|
|
|
|
<h3>Using Schema Types</h3>
|
|
The default SOAP encodings implement most XML built-in types, as well
|
|
as the basic SOAP types. In the absence of a specified type, native
|
|
values and objects will typically be correctly identified and mapped to a
|
|
corresponding schema type. Many types do not match, and so they will
|
|
be mapped to a close type. Providing specific schema types can help
|
|
the encoding produce the desired results. For example, multidimensional
|
|
arrays must be decoded as nested arrays because Javascript only supports
|
|
single-dimensional arrays. f no schema type is given that identifies
|
|
the array as multidimensional, then a multidimensional array will be encoded
|
|
as a nested array. An accurate schema type can also help when encoding
|
|
or decoding of other complex objects such as SOAP structs.<br>
|
|
<br>
|
|
Schema types may be attached to blocks before encoding or before accessing
|
|
the value of a returned object to better control the encoding and decoding.
|
|
All schema types which are used to control the encoding and decoding
|
|
should come from the schema collection available through any associated encoding.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Schema Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Get the schema collection of all associated encodings.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SchemaCollection></i></font>
|
|
= <font color="#993300"><i><SOAPEncoding></i></font> .schemaCollection;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Load additional schema types from XML Schema files
|
|
into the schema collection.<br>
|
|
<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SchemaLoader></i></font> =
|
|
<font color="#993300"><i><SchemaCollection></i></font> ;</li>
|
|
<li>// and then<br>
|
|
</li>
|
|
<li><font color="#993300"><i><SchemaLoader></i></font>.load("<font color="#993300"><i>
|
|
<schema file URI></i></font> ");</li>
|
|
<li>// or<br>
|
|
</li>
|
|
<li><font color="#993300"><i><SchemaLoader></i></font>.loadAsync("<font color="#993300"><i>
|
|
<schemaURI></i></font> ", <font color="#993300"><i><load completion
|
|
function></i></font>);</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Specify the XML Schema type to be used when encoding
|
|
or decoding a block -- decoding a block occurs when you get its value if
|
|
it came from an encoded message such as a SOAPResponse.</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SchemaType></i></font> = <font color="#993300"><i>
|
|
<SchemaCollection></i></font> .getType("<font color="#993300"><i><name></i></font>
|
|
", "<font color="#993300"><i><namespaceURI></i></font>");</li>
|
|
<li>if (<font color="#993300"><i><schemaType != null) {<br>
|
|
</i></font></li>
|
|
<font color="#993300"><i> <li> <font color="#993300"><i><SOAPBlock></i></font>
|
|
.schemaType = <font color="#993300"><i><SchemaType></i></font> ;</li>
|
|
<li>}</li>
|
|
</i></font>
|
|
</ul>
|
|
<font color="#993300"><i> </i></font></td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3></h3>
|
|
|
|
<h3></h3>
|
|
|
|
<h3>Customization of Encodings</h3>
|
|
A specific encoding must have encoders and decoders to function. Encoding
|
|
or decoding of data always begins with a default encoder or decoder, which
|
|
then may lookup additional encoders or decoders by a string key as required.
|
|
For either the 1.1 or 1.2 version of the default SOAP encoding, the
|
|
default encoder and decoder use the schema type's namespaceURI and name,
|
|
seperated by "#" to look up additional decoders for specific schema types.
|
|
Additional encoders and decoders registered within the default encodings
|
|
will automatically be invoked as an object identified as the corresponding
|
|
type is processed. Other encodings can use any scheme for looking up
|
|
additional encoders and decoders, or none at all if all the work is done by
|
|
the default encoder and decoder for that encoding style. Encodings which
|
|
are registered with the system, such as the default SOAP 1.1 or 1.2 encodings,
|
|
automatically come with encoders and decoders built-in, whereas new encodings
|
|
have none. Custom encodings may also reuse existing encoders, and decoders,
|
|
but there is no guarantee which are present, since the mapping may vary when
|
|
handling an infinite set of types with a finite set of encoders and decoders.<br>
|
|
<br>
|
|
Also, there has been a proliferation of variant schema types with different
|
|
URIs, which may be improperly intermixed in usage, but expected to function
|
|
properly. Most notably, the SOAP 1.1 specification used unofficial
|
|
XML Schema URIs and SOAP encoding schema URIs not compatible with those which
|
|
are in the W3C XML Schema and drafts for SOAP 1.2 specifications. It
|
|
is not uncommon to send and receive messages using the URIs specified in
|
|
the SOAP 1.1 specification, but described by WSDL using XML Schema that uses
|
|
the official, correct URIs. To solve these problems, the encoding permits
|
|
schema URIs to be aliased, both on input and on output, so that only the
|
|
SOAP 1.2 and official XMLSchema types are used internally, while supporting
|
|
the other URIs. Mappings of this type may be automatically built-in
|
|
for encodings which are registered with the system, such as the default encodings
|
|
of SOAP 1.1 and 1.2. The default URI mappings in the default encoding
|
|
of SOAP 1.1 may be manipulated, for example, to output the official XML Schema
|
|
URIs, without having to rewrite any encoders or decoders.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Encoding Customization Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Create a custom encoder.</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li>function <font color="#993300"><i><New DOM Element></i></font>
|
|
= <font color="#993300"><i><SOAPEncoder name></i></font>(<font color="#993300"><i>
|
|
<SOAPEncoding></i></font>, <font color="#993300"><i><value></i></font>
|
|
, <font color="#993300"><i><namespaceURI></i></font> , <font color="#993300"><i>
|
|
<name></i></font>, <font color="#993300"><i><SchemaType></i></font>
|
|
, <font color="#993300"><i><SOAPAttachments></i></font> , <font color="#993300"><i>
|
|
<Parent DOM Element></i></font> )</li>
|
|
<li>{</li>
|
|
<li>[...]</li>
|
|
<li><font color="#993300"><i><DOM Element></i></font> =
|
|
<font color="#993300"><i><Parent DOM Element></i></font> .ownerDocument.createElementNS(namespaceURI,name);<br>
|
|
</li>
|
|
<li>[...]</li>
|
|
<li> <font color="#993300"><i><Parent DOM Element></i></font>
|
|
.appendChild(<font color="#993300"><i><DOM Element></i></font>);<br>
|
|
</li>
|
|
<li> return <font color="#993300"><i><DOM Element></i></font>
|
|
;<br>
|
|
</li>
|
|
<li>}</li>
|
|
<li>// and</li>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font>.defaultEncoder
|
|
= <font color="#993300"><i><SOAPEncoder></i></font> ;</li>
|
|
<li>// or</li>
|
|
<li> <font color="#993300"><i><SOAPEncoding></i></font>.setEncoder("<font color="#993300"><i>
|
|
<namespaceURI></i></font> #<font color="#993300"><i><name></i></font>
|
|
",<font color="#993300"><i><SOAPEncoder></i></font>);</li>
|
|
|
|
</ul>
|
|
<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Create a custom decoder.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li>function <font color="#993300"><i><New DOM Element></i></font>
|
|
= <font color="#993300"><i><SOAPDecoder name></i></font>(<font color="#993300"><i>
|
|
<SOAPEncoding></i></font>, <font color="#993300"><i><DOM Element></i></font>
|
|
, <font color="#993300"><i> <SchemaType></i></font>, <font color="#993300"><i>
|
|
<SOAPAttachments></i></font>)</li>
|
|
<li>{</li>
|
|
<li>[...]</li>
|
|
<li> return <font color="#993300"><i><value or object></i></font>
|
|
;<br>
|
|
</li>
|
|
<li>}</li>
|
|
<li>// and</li>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font>.defaultDecoder
|
|
= <font color="#993300"><i><SOAPDecoder></i></font> ;</li>
|
|
<li>// or</li>
|
|
<li> <font color="#993300"><i><SOAPEncoding></i></font>.setDecoder("<font color="#993300"><i>
|
|
<namespaceURI></i></font> #<font color="#993300"><i><name></i></font>
|
|
",<font color="#993300"><i><SOAPDecoder></i></font>);</li>
|
|
|
|
</ul>
|
|
<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Map or unmap schema URI aliases<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPEncoding></i></font>.mapSchemaURI("<font color="#993300"><i>
|
|
<external URI></i></font> ", "<font color="#993300"><i><internal
|
|
URI></i></font>", <font color="#993300"><i><true to alias output></i></font>
|
|
);</li>
|
|
<li> // or</li>
|
|
<li> <font color="#993300"><i><SOAPEncoding></i></font>.unmapSchemaURI("<font color="#993300"><i>
|
|
<external URI></i></font>");</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Register modified or alternative encodings, making
|
|
them automatically available to all SOAP scripts in the system<br>
|
|
</td>
|
|
<td valign="Top">Install an appropriate registerable encoding in
|
|
<code> components/<font color="#993300"><i><new encoding></i></font>
|
|
.js</code><br>
|
|
</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>Security Operations</h3>
|
|
In browsers, the risk of allowing an externally-loaded untrusted script
|
|
to request information within a firewall and send it elsewhere has lead
|
|
to very tight sandbox restrictions, only permitting external browser scripts
|
|
to request xml data and services on the same domain from which the script
|
|
was loaded. This same restriction applies by default to SOAP requests
|
|
executed within the browser. This means that an externally-loaded
|
|
script cannot, for example, call other external services unless they are
|
|
in the same domain from which the page was loaded. Even if the page
|
|
was loaded from the user's own hard disk, the script must ask for permission
|
|
to make SOAP calls. A browser enhancement is planned to permit more-precise
|
|
control of trust between scripts and specific available services.<br>
|
|
<br>
|
|
Since SOAP permits headers to be added to messages that require interpretation
|
|
by the recipient, this API can request a header to warn the recipient that
|
|
it was sent by an untrusted script loaded from a specific sourceURI, and
|
|
no good SOAP service will unintentionally disregard the warning. If
|
|
the envelope is verified and the header is added, then the browser can allow
|
|
the script less=-restricted access to services outside of its source domain.
|
|
Accepting this header permits SOAP services that really do want to
|
|
be universally available to allow access without forcing the user to risk
|
|
breach of the firewall protections or requiring user intervention at all.<br>
|
|
<br>
|
|
|
|
<table cellpadding="2" cellspacing="2" border="1" width="100%">
|
|
<tbody>
|
|
<tr>
|
|
<td valign="Top">Security Operation<br>
|
|
</td>
|
|
<td valign="Top">How to Do It<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Mark the call with a verifySourceHeader, if the
|
|
server permits it, so the browser can make the call with less privilege
|
|
and risk.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li><font color="#993300"><i><SOAPCall></i></font>.verifySourceHeader
|
|
= true;</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Request risky privileges within a local or signed
|
|
script to make an unverified SOAP calls to other domains.<br>
|
|
</td>
|
|
<td valign="Top">
|
|
<ul>
|
|
<li>netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead")</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Modify the security settings in the preferences
|
|
file, allowing scripts from some domain to make risky SOAP calls to any
|
|
other domain, which is disabled by default.</td>
|
|
<td valign="Top">Add the setting in <code>default/pref/all.js</code>
|
|
:<br>
|
|
|
|
<ul>
|
|
<li>pref("<font color="#993300"><i><some domain prefix></i></font>
|
|
.SOAPCall.invoke","allAccess");</li>
|
|
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Modify the security settings in the preferences file
|
|
to disallow calls made with the verifySource header, which is generally enabled
|
|
by default.<br>
|
|
</td>
|
|
<td valign="Top">Change the setting in <code>default/pref/all.js</code>
|
|
:<br>
|
|
<ul>
|
|
<li>pref("capability.policy.default.SOAPCall.invokeVerifySourceHeader","none");</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td valign="Top">Register alternative transport mechanisms, making
|
|
available alternative transports to all scripts and perhaps creating alternative
|
|
security models for protocols besides http(s).<br>
|
|
</td>
|
|
<td valign="Top">Install an appropriate registerable encoding in
|
|
<code>components/<font color="#993300"><i><new transport></i></font>
|
|
.js</code>.<br>
|
|
</td>
|
|
</tr>
|
|
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3></h3>
|
|
|
|
<h2>Future Features</h2>
|
|
|
|
<h3>Access to SOAP as Proxies</h3>
|
|
Although a SOAP call can generally be accomplished using this low-level
|
|
API in a few dozen lines, WSDL is a standard that describes how all of
|
|
this could occur with no manual type and argument setup required. An
|
|
implementation is under development that instantiates web service proxies
|
|
complete with appropriate xpconnect interfaces by simply loading and using
|
|
information out of a WSDL file. The proxy's interface has dynamically-generated
|
|
methods named appropriately to match the services described in the WSDL file
|
|
which accept arguments of the appropriate data types and calls the appropriate
|
|
low-level SOAP functions with the appropriate type information, making
|
|
it even simpler not only to invoke services from Javascript code, but to
|
|
associate appropriate schema types loaded from the WSDL file with the arguments.
|
|
This higher level is not available in the first release. When
|
|
it is available, invoking WSDL-described features gets even easier and more
|
|
reliable.<br>
|
|
|
|
<h3> </h3>
|
|
|
|
<h3>Arbitrary Graphs of Data</h3>
|
|
The SOAP specification allows objects to be passed as arguments which
|
|
may have originally referenced other objects that are not owned in a pure
|
|
hierarchy. This is represented by using an href attribute. Due
|
|
to the problems with leaking reference counts in COM objects with cyclic
|
|
references, this has not been implemented yet. Also, the output of
|
|
a cyclicly-referencing set of objects has not been implemented. Incoming
|
|
objects that do not reference cyclicly currently create separate copies for
|
|
each reference to an object, and with cycles will probably never complete.
|
|
On input, hrefs are currently ignored. In the future it may be
|
|
possible to solve this and transmit and receive arbitrarily-referencing objects,
|
|
but the solution is more complex than just using weak references.<br>
|
|
|
|
<h3>SOAP With Attachments</h3>
|
|
Many clients and servers now support automatically transmitting large
|
|
Mime with a SOAP message by encapsulating it in MIME, DIME, or other enveloping
|
|
formats. This has been anticipated in the APIs, but the SOAPAttachments
|
|
API is currently a placeholder for this future feature which is not yet
|
|
implemented.<br>
|
|
|
|
<h3>New Transports and Local Services</h3>
|
|
Obvious new transports that would be useful include e-mail -- permitting
|
|
a SOAP exchange to occur as an email exchange --, instant messenger for
|
|
peer to peer, and a local manager with a controlled security model but without
|
|
the size limitations, enabling SOAP to save and restore arbitrary Javascript
|
|
application data on the client. These services require a framework,
|
|
already being planned, for permitting the browser to host services as well
|
|
as being a good client. There are obviously security issues to be
|
|
solved to make these successful.<br>
|
|
<h3>Standards</h3>
|
|
The interfaces to the objects of this API were designed to be as simple and
|
|
universal as possible. We believe that we should sponser a W3C proposal
|
|
to standardize an API for invoking this type of service from web clients.
|
|
We recognize that in such an effort, changes would be inevitable and
|
|
welcomed. Certain parts of the API are incomplete, especially the SOAPAttachments
|
|
object, which will serve to allow encoders and decoders to control uniqueness
|
|
and referencing both for resolving arbitrary graphs of data (when that is
|
|
implemented) as well as references to attached objects carried with the message
|
|
in an external encapsulation such as MIME or DIME (when that is implemented).<br>
|
|
|
|
<h2>Samples and Tests</h2>
|
|
Some samples or tests have been created, but these commonly only test the
|
|
basic operations. Most work but a few still predate the current SOAP
|
|
interfaces, and are located within the mozilla/extensions/xmlextras/tests
|
|
directory of the mozilla build. We welcome the contribution of tests
|
|
by other parties.<br>
|
|
<br>
|
|
A test server has been set up outside the firewall at ray.dsl.xmission.com
|
|
where services may be deployed to help test and demo the features for scripting
|
|
SOAP in Mozilla. This is a Tomcat server running the Apache SOAP code.<br>
|
|
<br>
|
|
Bugs should be reported as usual for the mozilla or derived product you are
|
|
using.<br>
|
|
<h2>Object Interfaces</h2>
|
|
<br>
|
|
<h2> </h2>
|
|
</div>
|
|
</div>
|
|
|
|
</body>
|
|
</html>
|