When using xpcom you are encouraged to think in terms of
interfaces and objects rather than callback functions -
regardless of the programming language you use.

So the 'normal' way to do something like this is...

// Declare the interface to receive callbacks
[scriptable, uuid(...)]
interface mynamespaceListener : nsISupports
{
  void connectionOpened(in PRBool success);
};

// Declare the interface that will send callbacks.
// You need to be able to tell it who to call...
[scriptable, uuid(...)]
interface mynamespaceNotifier : nsISupports
{
  // You could use explicit get/set. 
  // But attributes are easier...
  attribute mynamespaceListener connectionListener;
}; 

You implemenent the get/set methods of mynamespaceNotifier and do
an AddRef on the current listener (an nsCOMPtr class member is
good for this).

You expose that notifier object ot JS either as a service or more
directly by whatever means..

Your JS code then creates a JS object that has a connectionOpened
method and passes that object to the notifier object by setting
the notifier's connectionListener property.

There are pleny of styles of doing this, but you might do is
something like this...

var listener = {
  connectionOpened : function(success) {
    if (success)
      alert("The Connection was made!")
    else
      alert("The Connection Failed.");
  }
  // you can add whatever other properties you might want to this
object
};

// assuming that your notifer is a service...
var clazz = Components.classes["my_notifier_contractid"];
var iface = Components.interfaces.mynamespaceNotifier;
var notifier = clazz.getService(iface);

notifier.connectionListener = listener;

Now in your C++ code you can just call something like...
  mListener->ConnectionOpened(PR_TRUE);
...and the call will be routed to the JS code.

Note that if you really implement this object in a document then
you'd probably want to clear the listener then the page gets
unloaded. e.g....
  notifier.connectionListener = null;
This ought to make your C++ code Release the previous listener
and will cause xpconnect to allow the JS object to be garbage
collected when there are no other references to it.

I hesitate to tell you about it... but if you absolutely *need*
the JS code to be called back as a plain function rather than as
a method on an object then you *could* use some xpconnect
functionality that was very recently added to support legacy DOM
event handling. I discourage its use - there is no support for
doing this in any other language. The pattern is...

// Declare the interface to receive callbacks 
// ... using the 'function' keyword
[function, scriptable, uuid(...)]
interface mynamespaceListener : nsISupports
{
  void connectionOpened(in PRBool success);
};


In JS you can use a plain function..

function WebTerm_ConnectionOpened(success) 
{/*...*/}

The C++ code is the same.

You set the callback using:
notifier.connectionListener = WebTerm_ConnectionOpened;

When the call is made to the callback xpconnect will notice that
this is a [function] interface and attempt to call the object as
a function rather than lookup a named function property on the
object and call that as it would normally do.

Note that there is *no* support for...

notifier.connectionListener = "WebTerm_ConnectionOpened()";

This would imply that xpconnect would need to know how to convert
a string into a compiled function. It does not do that. The DOM
supports this using a bunch of specialized code.

Again, I *really* encourage you to use objects with named methods
which make the object meet the requirements of the declared
interface rather than use the less supported [function] hack.

Also, I just looked at your example below again and realized that
I showed you a more generic "set it and forget it" listener
pattern. For the specific case you have you could do pretty much
what I wrote above, but just declare your OpenConnection method
to take an 'in' param of type mynamespaceListener and pass your
listener object into the function as a param. Be sure to AddRef
and Release the interface pointer appropriately on the C++ side.

Also, you may have noticed xpidl's method name leading character
upper-casing rules. the deal is that we have history of using
LeadingUpperCaseMethods in C++ and leadingLowerCaseMethods in
JavaScript. If got decided (with *much* controversy!) a while
back to encourage people to declare their interfaces like:
"myMethod" in the idl and the C++ header mapping will get
"MyMethod" and the JS will use "myMethod". the xpidl compiler,
xpconnect and other such dynamic mapping layers deal with making
this work right. So anyway, you are encouraged to use the
"myMethod" style in your idl method and attribute names.

John.

Patrick McHale wrote:
> 
> Hi,
> 
> I was wondering about implementing CallBacks - Calling Javascript functions
> from an XPCOM object. I am not sure about how to correctly implement this.
> At present I have a Javascript function which I wish to call.
> 
> function WebTerm_ConnectionOpened(success) {
> if (success)
>       alert("The Connection was made!")
> else
>       alert("The Connection Failed.");
> }
> 
> I also have a function defined to intiate the open connection - with a
> Callback function description.
> 
> function OpenConnection(theSession) {
>     theSession.OpenConnection(true, 300 ,"WebTerm_ConnectionOpened()");
> 
> The open connection routine works fine - but would like to get the CallBack
> operation working.  How should the IDL file be setup?.  Is this to be
> defined as a inout attribute?.  What is the return parameter needed to
> enable a Callback function to work?.
> 
> Would be very grateful for some input on this.
> 
> Regards
> 
> Patrick McHale
> Powerlan USA

Reply via email to