Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

XPCOM cpp to js callback

19 views
Skip to first unread message

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 11:30:16 AM12/4/06
to dev-tec...@lists.mozilla.org
I have an XPCOM object built with C++, and I can successfully call into
it from js. However, I'm now trying to do some work asynchronously, and
need to call back into js upon completion of my task.

As an intermediate proof-of-technology step, I'm trying to do an
immediate callback to my js object, and this is failing. My apologies
for the length of this, but I figured it's better to give too much info
than too little.... A question or two is at the end of all this...

I've declared a new interface:

[scriptable, uuid(8FC0AAA9-3C3F-4377-8179-6EA50E7A8286)]

interface nsIPhishListener : nsISupports

{

void onLookupComplete(in nsICancelable aRequest,


in long aResult);

};

to go with my existing object (plus the asynch request):

[scriptable, uuid(EE5AA2A0-B27F-4e73-A62C-CA7A4E8201DC)]

interface IFFHook : nsICancelable

{

:

:

nsICancelable AsyncLookupPhish(in AUTF8String aHostName,

in nsIPhishListener aListener);

};

My js code, lifted and modified from a post elsewhere:

var listener =

{

QueryInterface : function(aIID)

{

alert("js: myListener: QueryInterface(" + aIID + ")\n");

if (aIID.equals(Components.interfaces.nsIPhishListener) ||

aIID.equals(Components.interfaces.nsISupports) )

{

alert("js: myListener: QueryInterface : ok\n");

return this;

}

throw Components.results.NS_NOINTERFACE;

Components.returnCode =
Components.results.NS_ERROR_NO_INTERFACE;

return null;

},

onLookupComplete: function( aRequest, aResult )

{

alert("js: myListener: onLookupComplete(" + aRequest + ", " +
aResult +")\n");

}

}

function MyComponentTestGo() {

try {


netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect")
;

const cid =
"@siteadvisor.com/SiteAdvisor/FFHook;1";

obj = Components.classes[cid].createInstance();

obj =
obj.QueryInterface(Components.interfaces.IFFHook);

} catch (err) {

alert(err);

return;

}

alert("js: Before AsyncLookupPhish");

obj.AsyncLookupPhish("www.siteadvisor.com", listener);

alert("js:After AsyncLookupPhish");

}

And the pertinent C++ header and source:

class CFFHook : public IFFHook

{

public:

NS_DECL_ISUPPORTS

NS_DECL_NSICANCELABLE

NS_DECL_IFFHOOK

CFFHook();

private:

~CFFHook();

protected:

void ReportComplete();

nsCOMPtr<nsIPhishListener> m_Listener;

------------------

NS_IMETHODIMP CFFHook::AsyncLookupPhish(const nsACString & aHostName,
nsIPhishListener *aListener, nsICancelable **_retval)

{

::MessageBox(NULL, _T("C++: AsyncLookupPhish 1"), _T("Foo"),
MB_OK);

m_Listener = aListener;

::MessageBox(NULL, _T("C++: AsyncLookupPhish 2"), _T("Foo"),
MB_OK);

// DEBUG DEBUG

ReportComplete();

return NS_OK;

}

void CFFHook::ReportComplete()

{

CString sz;

::MessageBox(NULL, _T("C++: Before OnLookupComplete"),
_T("Foo"), MB_OK);

nsresult nr = m_Listener->OnLookupComplete(this, 42);

sz.Format(_T("C++: OnLookupComplete returned %08X"), nr);

::MessageBox(NULL, sz, _T("Foo"), MB_OK);

m_Listener = NULL;

}

The QI on my object gets called for nsISupports guid by (I presume) FF's
XPCOM framework at the point of the "obj.AsyncLookupPhish" call, but
never again. I would have expected a QI call at the "m_Listener =
aListener;" assignment.

Then, the "OnLookupComplete" callback returns the not-so-helpful
0x80004005 (NS_ERROR_FAILURE), and no surprise, the js onLookupComplete
is not called.

Do I have to do some magic to register my new nsIPhishListener with
XPCOM, beyond putting the generated xpt into FF's component directory
and clearing the xpti and compreg files in my profile?

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 2:23:10 PM12/4/06
to dev-tec...@lists.mozilla.org

In an nutshell, for those who don't want to read through all that: I'm
trying to call back from C++ to a JavaScript object using an interface I
created with an idl etc. etc. based on many newsgroup threads and coding
examples I've found.

However, the C++ call that's supposed to be invoking the method on the
JavaScript object is returning with 0x80004005 (NS_ERROR_FAILURE).
Clearly, I'm getting into the XPCOM dispatch code, but it's not relaying
into my JS object.

What can cause this nondescript failure code?


--- snip ---

Nickolay Ponomarev

unread,
Dec 4, 2006, 2:37:39 PM12/4/06
to Paul_S...@mcafee.com, dev-tec...@lists.mozilla.org
I haven't seen this error, but did you check that the interface got
registered (by looking in xpti.dat or evaluating
Components.interfaces.nsIPhishListener in JS console).

Also am I right in assuming that all the JS code is privileged? (In
that case you don't need the enablePrivilege call).

Nickolay


>
> --- snip ---
> _______________________________________________
> dev-tech-xpcom mailing list
> dev-tec...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-tech-xpcom
>

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 2:48:14 PM12/4/06
to asqu...@gmail.com, dev-tec...@lists.mozilla.org
Yes, both of my interfaces are in xpti.dat. I opened Tools->Error
Console and eval'd "Components.interfaces.nsIPhishListener", got
"nsIPhishListener" in response; does that seem correct? Or should have I
gotten my guid instead?

Unfortunately, I don't have the background to answer your question about
the priviledge issue; I copied the test js from elsewhere.

John Bandhauer

unread,
Dec 4, 2006, 3:29:52 PM12/4/06
to
Are you so sure your callback is not getting called? If your js code
is not still the loaded document in some window then it's global
object might not still *have* an 'alert' method and *that* might be
the cause of the generic failure code.

Have you looked in the error console for interesting error info?

If XPConnect were having difficulty calling your method then you
should expect it to likely return a more interesting nsresult than the
generic failure code.

Your JS obj is not going to get as many QI calls as you expect.
XPConnect will try a QI for nsISupports to establish object identity
in some cases. But, inasmuch as JS objects are not even required to
implement QI, the fact that your object is not QI'd for your special
interface is not telling. XPConnect *trusts* you when you pass the
object as an instance of your interface. It builds a wrapper to
represent that interface for you and hands it to your C++ code.

I'm pretty sure that nsCOMPtr assignment does not require a QI call in
the normal case either. It is assigning typed pointers and doing ref
counting only.

John.

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 3:45:09 PM12/4/06
to jband_...@rattlebrain.com, dev-tec...@lists.mozilla.org
It's definitely still loaded, I'm doing this in a very simple test page
w/o any navigation or anything, and a subsequent JS alert is popping up
as expected.

My callback function is so far just a placeholder:

onLookupComplete: function( aRequest, aResult )
{
alert("js: myListener: onLookupComplete(" + aRequest + ", " + aResult

")\n");
}

.. so yeah, I *think* I'd know if it got called back. :-)


(I just tried wrapping the alert in a try/catch block, and it still
didn't get called. I also called the function directly, and I did get
the alert.)


Thanks for the info on the QI behavior, I've been doing COM for so long
I've come to expect things in XPCOM that don't necessarily hold.

And to reveal more of my newbie-ness: how do I open the error console in
FireFox?

Thanks!

-Paul


-----Original Message-----
From: dev-tech-xp...@lists.mozilla.org
[mailto:dev-tech-xp...@lists.mozilla.org] On Behalf Of John
Bandhauer
Sent: Monday, December 04, 2006 3:30 PM
To: dev-tec...@lists.mozilla.org
Subject: Re: XPCOM cpp to js callback

John.

Nickolay Ponomarev

unread,
Dec 4, 2006, 3:51:15 PM12/4/06
to Paul_S...@mcafee.com, dev-tec...@lists.mozilla.org
> And to reveal more of my newbie-ness: how do I open the error console in
> FireFox?
>
Tools -> Error (or JavaScript) Console

and you should read this:
http://kb.mozillazine.org/Setting_up_extension_development_environment#Set_development_prefs

Nickolay

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 4:06:47 PM12/4/06
to asqu...@gmail.com, dev-tec...@lists.mozilla.org
That setup page seems out of date, at least for my FF version 2.0: It
mentions browser.dom.window.dump.enabled which is not in my
about:config; it also refers to the -console flag, which gets a
complaint in the error log.
toJavaScriptConsole() is not defined, and dump() seems to be a no-op.

Anyway, I've sprinkled a lot of alert() calls in the js, and a bunch of
MessageBox in my C++, and they're all getting called (except for the one
that says my callback was called from C++).

-----Original Message-----
From: Nickolay Ponomarev [mailto:asqu...@gmail.com]
Sent: Monday, December 04, 2006 3:51 PM
To: Schmidt, Paul

Boris Zbarsky

unread,
Dec 4, 2006, 4:11:25 PM12/4/06
to
Paul_S...@McAfee.com wrote:
> That setup page seems out of date, at least for my FF version 2.0: It
> mentions browser.dom.window.dump.enabled which is not in my
> about:config

So? Only preferences which are actually currently set are in about:config.
That preference is not set by default; when it _is_ set, it has an effect. Just
set it in about:config.

> dump() seems to be a no-op.

Without setting browser.dom.window.dump.enabled it will be.

-Boris

Nickolay Ponomarev

unread,
Dec 4, 2006, 4:13:56 PM12/4/06
to Paul_S...@mcafee.com, dev-tec...@lists.mozilla.org
On 12/5/06, Paul_S...@mcafee.com <Paul_S...@mcafee.com> wrote:
> That setup page seems out of date, at least for my FF version 2.0: It
> mentions browser.dom.window.dump.enabled which is not in my
> about:config;

So add it.

> it also refers to the -console flag, which gets a
> complaint in the error log.

And still works. The message in the console is a known bug.

> toJavaScriptConsole() is not defined,

It is defined in browser.xul overlays. Where is it mentioned?

> and dump() seems to be a no-op.
>
Not if you run with -console and actually enable the said pref.

You need to set the prefs and check if there's anything interesting in
the JS console.

Nickolay

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 4:16:31 PM12/4/06
to bzba...@mit.edu, dev-tec...@lists.mozilla.org
Ah, I didn't realize I could create new prefs.


-----Original Message-----
From: dev-tech-xp...@lists.mozilla.org
[mailto:dev-tech-xp...@lists.mozilla.org] On Behalf Of Boris
Zbarsky
Sent: Monday, December 04, 2006 4:11 PM
To: dev-tec...@lists.mozilla.org
Subject: Re: XPCOM cpp to js callback

Paul_S...@McAfee.com wrote:
> That setup page seems out of date, at least for my FF version 2.0: It
> mentions browser.dom.window.dump.enabled which is not in my
> about:config

So? Only preferences which are actually currently set are in


about:config.
That preference is not set by default; when it _is_ set, it has an
effect. Just
set it in about:config.

> dump() seems to be a no-op.

Without setting browser.dom.window.dump.enabled it will be.

-Boris

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 4:21:02 PM12/4/06
to bzba...@mit.edu, dev-tec...@lists.mozilla.org
Ok, I have a console.

The only output there was what I put in a dump() call. No other warning
or error messages were generated by my attempt at calling back my
object.

So is this any more useful than alert(), beyond the message having
persistence?

Paul_S...@mcafee.com

unread,
Dec 4, 2006, 4:51:39 PM12/4/06
to Paul_S...@mcafee.com, jband_...@rattlebrain.com, dev-tec...@lists.mozilla.org
And just to see if there was a recursion lock built into XPConnect, I
started a timer in the AsynchLookup call and returned immediately. When
the timer fired, I tried to do the callback, with the same error code
resulting.
(Yes, it was on the same thread, my C++ object is now also a window, and
I used WM_TIMER.)

-----Original Message-----
From: dev-tech-xp...@lists.mozilla.org
[mailto:dev-tech-xp...@lists.mozilla.org] On Behalf Of Schmidt,
Paul
Sent: Monday, December 04, 2006 3:45 PM
To: jband_...@rattlebrain.com; dev-tec...@lists.mozilla.org
Subject: RE: XPCOM cpp to js callback

It's definitely still loaded, I'm doing this in a very simple test page
w/o any navigation or anything, and a subsequent JS alert is popping up
as expected.

My callback function is so far just a placeholder:

onLookupComplete: function( aRequest, aResult )
{
alert("js: myListener: onLookupComplete(" + aRequest + ", " + aResult
")\n");
}

.. so yeah, I *think* I'd know if it got called back. :-)


(I just tried wrapping the alert in a try/catch block, and it still
didn't get called. I also called the function directly, and I did get
the alert.)


Thanks for the info on the QI behavior, I've been doing COM for so long
I've come to expect things in XPCOM that don't necessarily hold.

And to reveal more of my newbie-ness: how do I open the error console in
FireFox?

Thanks!

-Paul


-----Original Message-----
From: dev-tech-xp...@lists.mozilla.org
[mailto:dev-tech-xp...@lists.mozilla.org] On Behalf Of John
Bandhauer
Sent: Monday, December 04, 2006 3:30 PM
To: dev-tec...@lists.mozilla.org
Subject: Re: XPCOM cpp to js callback

John.

Nickolay Ponomarev

unread,
Dec 4, 2006, 8:06:07 PM12/4/06
to Paul_S...@mcafee.com, bzba...@mit.edu, dev-tec...@lists.mozilla.org
It wasn't clear from your reply, did you look in the *JavaScript* (or
Error, depending on Firefox version) console and confirm there were no
messages there?

As for my privilege question, why does the code in MyComponentTestGo
run? Is it in a script for a chrome:// document? Or is it just a
webpage? Do you have to click through a security dialog before the
code gets to run?

Nickolay

> Ok, I have a console.
>
> The only output there was what I put in a dump() call. No other warning
> or error messages were generated by my attempt at calling back my
> object.
>
> So is this any more useful than alert(), beyond the message having
> persistence?
>
>
>

> -----Original Message-----
> From: dev-tech-xp...@lists.mozilla.org
> [mailto:dev-tech-xp...@lists.mozilla.org] On Behalf Of Boris
> Zbarsky
> Sent: Monday, December 04, 2006 4:11 PM
> To: dev-tec...@lists.mozilla.org
> Subject: Re: XPCOM cpp to js callback
>

> Paul_S...@McAfee.com wrote:
> > That setup page seems out of date, at least for my FF version 2.0: It
> > mentions browser.dom.window.dump.enabled which is not in my
> > about:config
>
> So? Only preferences which are actually currently set are in
> about:config.
> That preference is not set by default; when it _is_ set, it has an
> effect. Just
> set it in about:config.
>
> > dump() seems to be a no-op.
>
> Without setting browser.dom.window.dump.enabled it will be.
>
> -Boris

John Bandhauer

unread,
Dec 4, 2006, 8:18:10 PM12/4/06
to
FWIW, nothing in your code jumped out at me as being particularly wrong.

I would suggest that you confirm that there are no stale descriptions
of the interface on your system; e.g. previous attempts at the
interface using different UUIDs or names or method signatures. If the
typelib loader finds an xpt file that is not in sync with the idl that
is compiled into your component then odd things can happen. Also, make
sure your UUIDs are unique and not used by some other interface. These
are the sorts of problems that can come up as code evolves during
development. It is worth being sure that this is not what is going on
here.

The only other idea that comes to mind right now is something that may
not be very attractive to you as a component developer. But, if *I*
was trying to solve this problem then I'd use a debug build of the
browser and then trace into the call that your C++ component makes. It
*should* get into nsXPCWrappedJSClass::CallMethod. And from there lots
of fun stuff might happen.

Other than that, I don't know what to tell you. The general thing it
sounds like you are trying to do certainly works elsewhere. Perhaps
there is some subtle thing wrong with your code or your 'setup' that
is not obvious from here. Either figure out a way to debug it or
experiment with variations that might give you a clue to what thing
you might be doing wrong.

John.

Paul_S...@mcafee.com

unread,
Dec 5, 2006, 7:57:12 AM12/5/06
to asqu...@gmail.com, bzba...@mit.edu, dev-tec...@lists.mozilla.org
I'm on Firefox 2.0.

I ran Firefox -console, and the only message there was from a single dump() statement I put in the javascript. I've also opened Tools->Error Console, and nothing appears there either when I run my scripts.

Right now, the JS lives in a .js file referenced by an html file, both on my local disk. The page is just a button which invokes the js. No security dialog appears. (In the future, the code will live in chrome).


________________________________

From: Nickolay Ponomarev [mailto:asqu...@gmail.com]
Sent: Mon 12/4/2006 8:06 PM
To: Schmidt, Paul

Paul_S...@mcafee.com

unread,
Dec 5, 2006, 8:09:45 AM12/5/06
to jband_...@rattlebrain.com, dev-tec...@lists.mozilla.org
Every change, I copy any new dll and xpt files over to the ff components directory, and delete the xpti and compreg dat files from my profile directory. If there are any other cache files that need to be deleted, please enlighten me.

I didn't have this sort of problem when creating the js -> C++ portion of my XPCOM object; nevertheless I just generated a new GUID for the offending interface, and I'm getting the same behavior.

Yes, I have no doubt that, with a debug build of the browser, I'd fairly quickly solve the problem. However, last week as I was starting this project, I tried to build FF. I ran into problems right from the start: After installing cygwin and diddling my environment variables per instructions, running the makefile that was supposed to check out the latest code displayed all sorts of sed errors, then gacked. Since it wasn't obvious I actually needed to build the browser just to write an extension, and it looked like it was going to be a project all on its own, I gave up. If there is an easier method to building this puppy, again, please enlighten me.

-Paul

________________________________

From: dev-tech-xp...@lists.mozilla.org on behalf of John Bandhauer
Sent: Mon 12/4/2006 8:18 PM
To: dev-tec...@lists.mozilla.org
Subject: Re: XPCOM cpp to js callback

John.

John Bandhauer

unread,
Dec 5, 2006, 1:36:34 PM12/5/06
to
I'm short on ideas. Perhaps if you make a more complete version of
your code available then someone will see something. For instance, I
don't see the QueryInterface implementation of your C++ object. If
that method fails to handle the nsICancelable case then XPConnect will
fail to build a wrapper around it when it is passed to the JS code -
and the call would fail. There are other such possibilities :)

You could also just try adding a method with no params to the callback
interface, implement it in JS, and try calling that. i.e. get
*something* working and then figure out what is different about what
doesn't work.

John.

Paul_S...@mcafee.com

unread,
Dec 5, 2006, 2:03:17 PM12/5/06
to jband_...@rattlebrain.com, dev-tec...@lists.mozilla.org
Well, the no-parms approach got results. Looks like there were problems passing my C++ object into the js object. Don't know why, not going to spend any time figuring it out.

Instead of passing XPCOM objects, I'll just use a cookie technique.

Thanks to all.

________________________________

From: dev-tech-xp...@lists.mozilla.org on behalf of John Bandhauer
Sent: Tue 12/5/2006 1:36 PM
To: dev-tec...@lists.mozilla.org
Subject: Re: XPCOM cpp to js callback

John.

John Bandhauer

unread,
Dec 5, 2006, 2:31:19 PM12/5/06
to
It's fine if you are ready to move on. But, if you didn't already
confirm that your C++ QueryInterface implementation handles the
nsICancelable case then I suggest you take a moment to do so. An
improperly implemented C++ xpcom object might bite you again later.

John.

John Bandhauer

unread,
Dec 5, 2006, 2:33:05 PM12/5/06
to
Ah, also nsICancelable seems to not be a frozen interface. So, you
might think twice about including it in your object's inheritance
chain anyway.

John.

timeless

unread,
Dec 10, 2006, 5:13:31 AM12/10/06
to
just some random unrelated notes:

> interface IFFHook : nsICancelable

it'd be best for the protection of your object if it was iffIHook or
something where iff is any lowercase pair or tripple of letters, the I
is an I and the Ho is Caps followed by Lower.

> nsICancelable AsyncLookupPhish(in AUTF8String aHostName,

idl methods and attributes really should be declared interCaps not
InitialCaps.

> QueryInterface : function(aIID)

for debugging it's a good idea to name your functions (then you can try
using venkman without being as confused, among other tools)


strange:
> throw Components.results.NS_NOINTERFACE;
unreachable code:
> Components.returnCode =
> Components.results.NS_ERROR_NO_INTERFACE;
that confuses people
> return null;

> obj = Components.classes[cid].createInstance();
> obj =
> obj.QueryInterface(Components.interfaces.IFFHook);

you should combine those as:

> obj = Components.classes[cid].createInstance(Components.interfaces.IFFHook);

>

Don't use MessageBox, it's *dangerous*. Seriously. (read about
windbgdlg to understand why, but in short you're likely to get your app
killed.)


> ::MessageBox(NULL, _T("C++: AsyncLookupPhish 1"), _T("Foo"),
> MB_OK);

Instead use NS_DebugBreak or nsDebug::whatever or something in that
family.

> The QI on my object gets called for nsISupports guid by (I presume) FF's
> XPCOM framework at the point of the "obj.AsyncLookupPhish" call, but
> never again. I would have expected a QI call at the "m_Listener =
> aListener;" assignment.

Note that you really really really really should not be defining
components from dom. they should be written as js components that live
in a components directory. such components do not have DOM globals at
all (and are therefore safer for use as callbacks). This means no
access to window.alert and friends. Use Components.utils.reportError or
whatever to send a message to the Error Console.

At the very least, it'd be good to know where your js component lives
(chrome/, components/).

I'm sorry that I'm jumping in late. So many newsgroups....

timeless

unread,
Dec 10, 2006, 5:23:40 AM12/10/06
to
Paul_S...@McAfee.com wrote:
> I ran Firefox -console, and the only message there was from a single dump() statement I put in the javascript. I've also opened Tools->Error Console, and nothing appears there either when I run my scripts.
>
> Right now, the JS lives in a .js file referenced by an html file, both on my local disk. The page is just a button which invokes the js. No security dialog appears. (In the future, the code will live in chrome).

Nickolay was trying fairly hard to tell you about tools>error console

be sure that this (hidden pref!) is set to true:
javascript.options.showInConsole

but more seriously, you want to install and enable venkman (and be sure
to configure it to debug chrome!)

Using an html file is really not a good idea. You're much better off
using a component from the start. You can actually use
Components.manager.nsIComponentRegistrar.autoRegister(xpcom_local_file_object)
to register new objects on the fly, so you could really go through 20
iterations w/o restarting (although I *really* wouldn't recommend it).

timeless

unread,
Dec 10, 2006, 5:33:57 AM12/10/06
to
John Bandhauer wrote:
> Ah, also nsICancelable seems to not be a frozen interface. So, you
> might think twice about including it in your object's inheritance
> chain anyway.

instead of thinking twice. just don't do it :|.

it's against a modern xpcom law to extend someone else's interface
(excluding nsISupports). Extending them tends to result in some really
bad multiple inheritance. (The classic example involves
nsIBrowserHistory3 or something, really not a pretty sight.)

[I say modern because when jband was originally working on gecko we
weren't very explicit about saying don't do it. we've now learned that
being explicit about don'ts is important :(]

0 new messages