Cross-window background code... Ideas/Explanation?
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
Cross-window background code... Ideas/Explanation?
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)?
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)?
-
- Posts: 846
- Joined: August 4th, 2003, 9:32 am
Well, I can't help with all of your questions, but I can help with this one:
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
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
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
-
- Posts: 4019
- Joined: November 16th, 2003, 3:05 am
- Location: Russia, Moscow
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.)
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.)
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
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...
-
- Posts: 4019
- Joined: November 16th, 2003, 3:05 am
- Location: Russia, Moscow
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().
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.
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.
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
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?
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?
-
- Posts: 4019
- Joined: November 16th, 2003, 3:05 am
- Location: Russia, Moscow
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).
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
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
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
- BenBasson
- Moderator
- Posts: 13671
- Joined: February 13th, 2004, 5:49 am
- Location: London, UK
- Contact:
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
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