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

Talk about add-ons and extension development.
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

You could just use the wrappedJSObject trick in that case - interfaces make the code look cleaner, but if you need to just share a JS object across windows, wrappedJSObject may be the way to go (it's certainly easier).
User avatar
BenBasson
Moderator
Posts: 13671
Joined: February 13th, 2004, 5:49 am
Location: London, UK
Contact:

Post by BenBasson »

I did think about it, but my components don't seem to register without the IDL. I might have another look at getting that to work though. I'd rather send back arbitrary objects to save time (and save on getter methods), but it's not critical.
TheOneKEA
Posts: 4864
Joined: October 16th, 2003, 5:47 am
Location: Somewhere in London, riding the Underground

Post by TheOneKEA »

This thread ought to be stickied. I agree with asqueella that good documentation on creating XPCOM components using JavaScript is thin on the ground, and using a sticky to collect information that could make its way into the MozillaZine KB would be invaluable to all extension developers.
Proud user of teh Fox of Fire
Registered Linux User #289618
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

I did think about it, but my components don't seem to register without the IDL.

They should. It works for me.
richwklein
Posts: 331
Joined: November 24th, 2002, 8:20 pm
Location: Iowa
Contact:

Post by richwklein »

Cusser wrote: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 :)


You could try using an nsIVariant interface for your data.

XPConnect has magic to transparently convert between nsIVariant and JS types.


so at the top of your idl, you would declare the interfaces used like:

<pre>
interface nsIVariant;
</pre>

then in the interface you are defining you would do something like:

<pre>
nsIVariant getData(in PRUint32 aType);
</pre>
My Extensions:
<a href="http://forecastfox.mozdev.org">Forecastfox</a>
<a href="http://tipbar.mozdev.org">Tip of the Day</a>
<a href="http://urlnav.mozdev.org">Location Navigator</a>
<a href="http://finder.mozdev.org">Finder</a>
<a href="http://rsszilla.mozdev.org">RSSzilla</a>
Daniel_Orner
Posts: 118
Joined: May 26th, 2005, 11:06 am

Post by Daniel_Orner »

While we're here... I noticed this:

I've never used IDL to define an XPCOM object


The instructions I'm looking at (Creating XPCOM Components) say that you need both an IDL and a JS file to define an XPCOM object. Can it be done without an IDL file? If so, how?
richwklein
Posts: 331
Joined: November 24th, 2002, 8:20 pm
Location: Iowa
Contact:

Post by richwklein »

You only need an idl when you define a new interface. If you are using existing interfaces, it isn't needed.
My Extensions:
<a href="http://forecastfox.mozdev.org">Forecastfox</a>
<a href="http://tipbar.mozdev.org">Tip of the Day</a>
<a href="http://urlnav.mozdev.org">Location Navigator</a>
<a href="http://finder.mozdev.org">Finder</a>
<a href="http://rsszilla.mozdev.org">RSSzilla</a>
Daniel_Orner
Posts: 118
Joined: May 26th, 2005, 11:06 am

Post by Daniel_Orner »

How do you access an XPCOM object in JS without an interface? O_o
old zeniko
Posts: 0
Joined: December 31st, 1969, 5:00 pm

Post by old zeniko »

You can't. However for what you apparently want to do, you don't have to use your JS object as an XPCOM object. If you look closely at the <a href="jar:http://mozilla.dorando.at/keyconfig.xpi!/components/keyconfig-service.js">keyconfig component</a>, you'll note how the object is actually injected into each window:

Code: Select all

aSubject.keyconfig = {service: this};

For the domwindowopened notification, aSubject is the newly opened window. So you can access your object inside each window as if it were created in an overlay to browser.xul (i.e. as if you had written var keyconfig = { service: { /* your component here */ } }; in your extensions overlay code).

In fact, you could even write

Code: Select all

aSubject.yourObject = this;

in your component's observe method. That'd be the final equivalent to

Code: Select all

var yourObject = { ... };

in your "normal" code.
Daniel_Orner
Posts: 118
Joined: May 26th, 2005, 11:06 am

Post by Daniel_Orner »

OK, looks like a great idea! Thanks!

EDIT: As I thought, I'm running into problems. -_- I'm getting "histree is not defined" in my browser windows. I put the following file (which I named "histree-service.js") in the components subdirectory... any idea what's going wrong?
By the way, I tried putting an alert statement in the "initHistree" function, and it didn't fire. Not sure if that's because the function isn't getting called (even though it's right there in the domwindowopened observer) or because the window isn't defined for some reason...?

Code: Select all

function NSGetModule(compMgr, fileSpec) { return histreeModule; }

var histreeModule = {
  CID: Components.ID("{4a4bb6c4-94e4-11da-956a-00e08161165f}"),
  contractID : "@mozilla.org/histree;1",
  className  : "histreeService",

  registerSelf: function (aComponentManager, aFileSpec, aLocation, aType)
  {
    aComponentManager = aComponentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    aComponentManager.registerFactoryLocation(this.CID, this.className, this.contractID, aFileSpec, aLocation, aType);

    var CategoryManager = Components.classes["@mozilla.org/categorymanager;1"]
                                    .getService(Components.interfaces.nsICategoryManager);
    CategoryManager.addCategoryEntry("app-startup", this.className, "service," + this.contractID, true, true, null);
  },
 
  getClassObject: function (aComponentManager, aCID, aIID)
  {
    if (!aIID.equals(Components.interfaces.nsIFactory)) throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    if (aCID.equals(this.CID)) return this.factory;
   
    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  factory: {
    createInstance: function (aOuter, aIID)
    {
      if (aOuter != null) throw Components.results.NS_ERROR_NO_AGGREGATION;

      return new histreeService();
    }
  },
 
  canUnload: function () { return true; }
};

function histreeService() { }

histreeService.prototype = {
  observe: function (aSubject, aTopic, aData)
  {
     var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
    if(aTopic == "app-startup")
     {   os.addObserver(this,"domwindowopened",false);
      os.addObserver(this, "domwindowclosed", false);
     }
    else if(aTopic == "domwindowopened")
    {
      aSubject.histree = this;
     aSubject.histree.initHistree();
     aSubject.addEventListener("load", histree.newWindow, false);
    }
   else if (aTopic == "domwindowclosed")
   {
      for (var i = 0; i < aSubject.histree.windows.length; i++)
      {   if (aSubject.histree.windows[i].window == aSubject)
         {   aSubject.histree.windows.splice(i, 1);
            return;
         }
      }
   }
  },
//////more attributes/functions which were copied and pasted from my original object
}
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

How do you access an XPCOM object in JS without an interface? O_o

via wrappedJSObject, see above.
Daniel_Orner
Posts: 118
Joined: May 26th, 2005, 11:06 am

Post by Daniel_Orner »

Nowhere in this thread does it actually explain what the "wrappedJSObject trick" is, though everyone's referencing it all the time. -_-
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

It was mentioned in the previous thread on this topic: http://forums.mozillazine.org/viewtopic ... 74#1863874

[edit]
basically:

Code: Select all

// in the component code you set the magical property wrappedJSObject on the component instance in constructor:
function MyComponentImpl {
  this.wrappedJSObject = this; // or any other JS object
}
MyComponentImpl.prototype = {
  l: 1,
 // ...
};

// then in the caller you can access that object easily:
var o = Components.classes[mycontractid].getService/createInstance().wrappedJSObject;
alert(o.l); // alerts 1
Daniel_Orner
Posts: 118
Joined: May 26th, 2005, 11:06 am

Post by Daniel_Orner »

OK... I'm slowly getting there. But I've got this very weird error that just popped up.

Part of my component code:

Code: Select all

//Called on startup.
initHistree: function(aWindow)
{ /*...*/
   aWindow.addEventListener("load", this.newWindow, false);
}

/**... stuff... **/

//Called when a window is created.
newWindow: function(aEvent)
{   aEvent.currentTarget.removeEventListener("load", this.newWindow, false);   
   var win = aEvent.currentTarget.top;
   if (!win.getBrowser) return; //not a browser window
   this.addWindowListeners(win);
},
/*** More stuff... ***/
addWindowListeners: function(aWindow)
{ /**...**/
}


The weird thing is that the term "this" inside newWindow suddenly seems to be referring to the ChromeWindow object, not my component object! So addWindowListeners isn't defined any more. I can't use a reference to the object itself (as defined in my overlay code) since it doesn't know about it yet. -_- I tried replacing "this" with histreeService.prototype (histreeService being my component object), but I then simply get two "uncaught exception: null" in the JS console.

I am STUMPED.
asqueella
Posts: 4019
Joined: November 16th, 2003, 3:05 am
Location: Russia, Moscow

Post by asqueella »

Locked