Talk about add-ons and extension development.
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.
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)?
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
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).
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.)
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...
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...
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.
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.
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?
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).
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).
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.
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...
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
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
Who is online
Users browsing this forum: No registered users and 2 guests