Cross-window background code... Ideas/Explanation?

Talk about add-ons and extension development.
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Cross-window background code... Ideas/Explanation?

Post by BenBasson »

Okay guys, I need some help with how to approach something that I'm working on... basically it's a Folding@Home monitor for Firefox.

My design goals are:
- Reusable code that works in the background, independent of the window being used, instantiated only once.
- Visible status-bar UI for Firefox.
- Reusable status window launched by status-bar UI or as a standalone XULRunner application.

I think writing my code as an XPCOM component (in JavaScript) would help here, but I don't actually know anything about it. I can probably manage the implementation, there are some tutorials and sample projects out there, I just need someone to explain it to me conceptually...

My questions are:

1. How do you register/initialise the component? Do you do this when the first window is opened and have subsequent windows check for and skip this stage?

2. How well does this work with an object oriented design? I want to keep it as OO as possible and then have the clients simply look up the data, so for example, I'd have a single FoldingMonitor containing 0 or more FoldingClient objects. How would I work with this kind of design at a conceptual level?

3. Can this be logically divided as I propose above (i.e. within my design goals)?
ericjung
Posts: 846
Joined: August 4th, 2003, 9:32 am

Post by ericjung »

Well, I can't help with all of your questions, but I can help with this one:

1. How do you register/initialise the component? Do you do this when the first window is opened and have subsequent windows check for and skip this stage?

Take a look at this thread. I followed this for a PasswordMaker component, and I know azzer used it for a FoxClocks component. You simply write your JS component and place it in a "components" directory in your XPI. The components dir goes at the same level as chrome/ and defaults/. Note this doesn't work for Mozilla Suite :(
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

Thanks, that should get me started. I'm not interested in supporting the Mozilla Suite anyway, but I'll probably support SeaMonkey when it's ported to toolkit and compatibility comes for free (or cheap).
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

It works vey well with an object oriented design, and yes, you'll be able to implement the exact scheme you described here. (use Component.classes[your_contract_id].getService() to get a singleton component from all the windows).

Should you have any problems with this, feel free to ask here, I'll try to help.

(Someone should definitely write an article about components on devmo. The CXC book is too elaborate and most of it is for C++, and there's no really good tutorial for XPCOM components in JS for use in extensions.)
ericjung
Posts: 846
Joined: August 4th, 2003, 9:32 am

Post by ericjung »

Along similar lines, asqueella, do you have any ideas how I might get Firefox to use my component when new XmlHttpRequest() is called by JS? It implements nsIXMLHttpRequest and nsIJSXMLHttpRequest...
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

asqueella wrote:Should you have any problems with this, feel free to ask here, I'll try to help.

I'm still unsure at a conceptual level, how I'm going to do this stuff. For instance, once the MyComponent.prototype is written, can I skip the rest of that registration code below? I don't know what any of it is for. Is it sufficient to write the code out and shove it in the components subdirectory, then use it as I use any other XPCOM?

Perhaps I should go and pull apart ForecastFox ;)

[Edit] Right, did that. The registration code is included after each component's code, is it actually necessary? It seems a bit redundant to include it over and over (although this will work fine for me, since I'll probably only have one component).

One other thing... do I put my "class" (i.e. function and prototype) definitions in individual components? I'm going to need my shared component (effectively, the "FoldingMonitor" as above) to create and hold references to FoldingClient objects, as well as collect info about them. Where should I logically put my prototypes in this case?

Sorry for missing the point a bit here, I'm sure I'll get it eventually, it's just not clicking right now...
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

grimholtz: if you want to override XMLHttpRequest object, register your component with the same contract id but a different cid. This should do it.

Cusser: You usually just copy/paste the registration code from another place. In case of my example (kindly linked by grimholtz), you can have one copy of hte registration code, and just list your "classes" in var objects = [MyComponent]; array.

The idea of my reg. code is that you define simple JS "classes" with four special properties - classID, contractID, classDescription, and QueryInterface. Then you just pass all the objects to my registration code, place the JS file to an appropriate folder, and after a restart (and possibly deleting compreg.dat) your objects can be instantiated via XPCOM's Components.classes[contractID].createInstance/getService().

(So, answering your question, this lets you define several components in a single file easily, and you should probably define them in a single file.)

If you have spare time, you can read the creating XPCOM components book, which explains what the registration code does.

One caveat is that what you get from createInstance is actually a wrapper around the component's JS object. So without the wrappedJSObject trick, the only properties available on that wrapper are those defined on the interfaces you explicitly QI'ed to in the caller. E.g. "var o=Components.classes[contractID].createInstance(Components.interfaces.nsISomeInterface)" makes the properties/methods listed in nsISomeInterface's definition available through o. You might want to use the wrappedJSObject while writing a prototype code.

The reason you need all this is that XPCOM components exist outside of any window, and you can get a singleton component using getService().

One other thing... do I put my "class" (i.e. function and prototype) definitions in individual components?

The instances of "classes" are components. The files define what's called "modules", which are collections of components, basically.

this was probably hard to read, that's because a bit late, sorry about that.
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

asqueella wrote:this was probably hard to read, that's because a bit late, sorry about that.

Not at all, thanks for your help. I think I understand it now... I'll post back tomorrow or Monday with how I get on.
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

Right, I've got things working to my satisfaction (thanks!), but one thing still eludes me. One of my components is meant to run as a service, so I'm instantiating / referencing it with getService().

The service creates instances of a second component using createInstance(). If and when an instance is no longer required, how do I destroy it? I don't want to leak and I'm worried that simply dereferencing it isn't enough.

[Edit] I also want to return generic objects from certain functions. Is there a data-type that I can use in the IDL that would work for this?
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

All you need to do is make sure there are no references to the component you want to go away. That is there should be no properties on reachable objects that point to the component in JS, and no other existing components should reference your component (this can happen when you register your component as a callback, for example for nsITimer).
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

Okay, should be simple enough. My main component is the only thing that will hold long term references to the child components. Other code referencing it will only do so on a short-term basis (probably local variables as pointers at most).

Any ideas regarding the bit I edited in afterwards? I've declared my own struct in the IDL, but I'm not sure where or how to implement this. If I simply create a JavaScript object (via a prototype) in the module JS file (containing the component classes) will that be accessible via the code? Bear with me, I'll get the hang of this sooner or later, I'm finding it a bit scary outside of the typical extension comfort zone :)
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

I don't think you can define a method in IDL as returning an arbitrary JS object. After all, IDL is supposed to be language independent. To get a definite answer, you should ask some XPConnect guru.
ericjung
Posts: 846
Joined: August 4th, 2003, 9:32 am

Post by ericjung »

I've never used IDL to define an XPCOM object, but OMG IDL has an any type. MIDL (used for MS COM and DCOM) defines a byte type. Both define structs and enums...
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

I tried the "any" type, but XPIDL rejected it. I'm not really sure on it's use. I also tried using a struct, but it seems that the syntax requires that a struct is declared inside a module, which I'm not using... I'm declaring the interfaces globally, since doing otherwise appears to break their registration.

Ultimately, I can play with this later, but thanks anyway. For now, I intend to use methods returning arrays or single values, it's easier and I need to get on with the actual implementation :)
ericjung
Posts: 846
Joined: August 4th, 2003, 9:32 am

Post by ericjung »

I understand. If your component will only ever be used by your own code (i.e., you're not writing a shared component), you can hack and kludge your way through the interface--no one else will ever use it :)
Locked