this.__proto__ = Ci[].createInstance and QueryInterface calling problem
Axel Hecht <[email protected]> Mon, 04 Feb 2008 18:53:44 +0100
| Newsgroups | gmane.comp.mozilla.devel.rdf |
|---|---|
| Message-ID | <[email protected]> |
Asking js-engine for some xpconnect help. I left all the context in, I guess it's best to read from the bottom up for those in js-engine. Axel mcben wrote: > On 4 fév, 15:47, Axel Hecht <[email protected]> wrote: >> mcben wrote: >>> On 4 fév, 14:52, Axel Hecht <[email protected]> wrote: >>>> mcben wrote: >>>>> On 4 fév, 09:27, Axel Hecht <[email protected]> wrote: >>>>>> mcben wrote: >>>>>>> Hi folks, >>>>>>> Here is quite a long post about a problem with a own datasource >>>>>>> implementation based on a xml-datasource. >>>>>>> I'm looking for any clues or links that could help me to achieve my >>>>>>> first goal : a custom datasource that i could fill from a CSV file. >>>>>>> Thanks in advance for reading me :) >>>>>>> I'm creating a javascript component that fetches a CSV file with >>>>>>> XMLHttpRequest, parses it, then adds lines as RDF resources in a RDF >>>>>>> datasource. >>>>>>> Its contractid is like "@mozilla.org/rdf/datasource;1?name=mydatasource" >>>>>>> so as to get it in the chrome with the "rdf:mydatasource" keyword. >>>>>>> I started this work by using an in-memory-datasource as the __proto__ >>>>>>> properties of my javascript prototype. >>>>>>> I could fetch my file and appends data in the datasource : the result >>>>>>> was shown in a rdf tree. >>>>>>> The first problem with this design was that i was not able to overwrite >>>>>>> some nsIRDFDataSource methods (not a real problem in fact), nor extends >>>>>>> the in-memory-datasource component with other interfaces : the QI method >>>>>>> of my component was never called, so i got a NS_ERROR_NO_INTERFACE >>>>>>> error, when i QI another interface (eg: nsIDOMListener to adds >>>>>>> XMLHttpRequest callback in my chrome). >>>>>>> Then i took a look a the rdf:localstore C++ implementation >>>>>>> (nsLocalStore.cpp) and i'm quite happy with its design : it works like a >>>>>>> wrapper around a xml-datasource. It "justs" load data from >>>>>>> localstore.rdf in the profile directory or creates it if it does not exist. >>>>>>> Then with that, I could even read and store a cached version of my CSV >>>>>>> file on disk. >>>>>>> So, I've made the same design in my javascript component, all >>>>>>> nsIRDFDataSource related methods are forwarded to a "mInner" >>>>>>> xml-datasource property. >>>>>>> But when i try to call RegisterDataSource to register my datasource in >>>>>>> the constructor of my component, it throws a NS_ERROR_FAILURE : >>>>>>> Here is the code : >>>>>>> this._loadLocalData(); >>>>>>> RDF.RegisterDataSource(this, false); >>>>>>> this._loadRemoteData(); >>>>>>> If i log "this" the console service returns me that is an Object. >>>>>>> That's the problem ? >>>>>>> In fact I get from the chrome : [xpconnect wrapped (nsISupports, >>>>>>> nsIRDFDataSource, nsIRDFRemoteDataSource)] after the registration, even >>>>>>> if the registration fails. >>>>>>> But, the nsIRDFRemoteDatasource Init method is called at every calls >>>>>>> from chrome. >>>>>>> Maybe the real problem comes from the xml-datasource initialization. >>>>>>> The following code is called by the constructor of my component : >>>>>>> this.mInner = Components >>>>>>> .classes["@mozilla.org/rdf/datasource;1?name=xml-datasource"] >>>>>>> .createInstance(Components.interfaces.nsIRDFDataSource); >>>>>>> var remote = this.mInner >>>>>>> .QueryInterface(Components.interfaces.nsIRDFRemoteDataSource); >>>>>>> remote.Init(filespec); >>>>>>> The "Init" call throws an exception with the following code >>>>>>> "0x804b000a". Its related with nsISocketTransport and means >>>>>>> STATUS_WAITING_FOR. Ok, why not. >>>>>>> Then remote.loaded returns false, and calling remote.Refresh(false) >>>>>>> throws a NS_ERROR_FAILURE >>>>>>> What is wrong with that ? >>>>>>> So i catch this Init exception and get my remote datasource with the RDF >>>>>>> Service like this : >>>>>>> remote = RDF.GetDataSource(filespec) >>>>>>> .QueryInterface(Components.interfaces.nsIRDFRemoteDataSource) >>>>>>> this.mInner = remote >>>>>>> .QueryInterface(Components.interfaces.nsIRDFDataSource); >>>>>>> In that case no problem with remote ds, but the RegisterDataSource go on >>>>>>> failing. >>>>>>> And later, in the chrome, when i call a method or property that is >>>>>>> forwarded to the inner, i got back a undefined result. >>>>>>> And i cant look any result in my xul tree. >>>>>>> I can post the full source code of the component if needed. >>>>>> I suspect that it has something to do with your move from an in-memory >>>>>> datasource as inner to an rdf/xml as inner. I didn't get why you made >>>>>> that move, was that intentional? >>>>> Hi Axel, >>>>> Thank you for your answer. >>>>> To begin, you can grab my component source code here, with the "second >>>>> design" implementation (with inner): >>>>> http://pastebin.mozilla.org/319272 >>>>> And the xul tree that should show my results look quite simple : >>>>> <tree id="itemtypes-tree" flex="1" >>>>> datasources="rdf:bi-itemtypes" ref="bi:itemtypes-list"> >>>>> <treecols> >>>>> <treecol id="col-name" primary="true" label="Name" flex="1"/> >>>>> </treecols> >>>>> <template> >>>>> <treechildren> >>>>> <treeitem uri="rdf:*"> >>>>> <treerow> >>>>> <treecell label="rdf:*" /> >>>>> </treerow> >>>>> </treeitem> >>>>> </treechildren> >>>>> </template> >>>>> </tree> >>>>> Hum. yes, the move was partially intentional : >>>>> * My first try with an in-memory datasource was with the instance set >>>>> as __proto__ property in the prototype of my component. That is to >>>>> say, that it was not with the "inner" design. >>>>> The main problem with this design was that i could not extends my >>>>> component by implementing other public interface : the QI function of >>>>> my component was never called, nor any method that i though they >>>>> overrode existing methods of the in-mem. The only thing that i could >>>>> improve in this design was the init phase of my component (by filling >>>>> in-mem with XMLHttpRequest) nothing else. >>>>> Is it normal that with the following code, the QueryInterface method >>>>> is never called, that's why i cant implement any other interfaces ? (i >>>>> think that the __proto__ QI is called instead). >>>>> function myComponentImplementation() { >>>>> } >>>>> myComponentImplementation.prototype { >>>>> __proto__ : Components.classes["@mozilla.org/rdf/datasource;1? >>>>> name=in-memory-datasource"] >>>>> .createInstance(Components.interfaces.nsIRDFDataSource), >>>>> QueryInterface : function(iid) { >>>>> if (!iid.equals(C_i.nsIDOMEventListener) && >>>>> !iid.equals(C_i.nsIRDFDataSource) && >>>>> !iid.equals(C_i.nsIRDFRemoteDataSource) && >>>>> !iid.equals(C_i.nsISupports)) >>>>> throw Components.results.NS_ERROR_NO_INTERFACE; >>>>> ConsoleService.logStringMessage("QI called:" + iid); >>>>> return this; >>>>> } >>>>> } >>>>> * With the "inner" design, i immediatly use a xml-datasource, just >>>>> like the nsLocalStore implementation. With that, i could load a cached >>>>> version of the rdf file from disk, and then update it with >>>>> XMLHttpRequest only if it really needed. >>>>> Since, you are thinking that the problem can come from the xml- >>>>> datasource, i took the same component, and just replaced the xml- >>>>> datasource inner, with an in-memory-datasource. >>>>> Indeed, it seems there is no major failure with initialization, i can >>>>> register the datasource, but the content that i downloaded with >>>>> XMLHttpRequest and that i put in the ds is not reflected in my chrome >>>>> xul tree, nor i can query my datasource for any target that sould >>>>> exists. >>>>> More precisely, about any errors, i had to create the container (the >>>>> one to wich I append downloaded resources) with this.mInner (the in- >>>>> mem ds) and not my component. Is it normal ? >>>>> Look: >>>>> this._container = RDFContainer.MakeSeq(this.mInner, >>>>> RDF.GetResource(this._struct.ref)); >>>>> Even though my component should implement nsIRDFDataSource, that's why >>>>> i thought i could do that : >>>>> this._container = RDFContainer.MakeSeq(this, >>>>> RDF.GetResource(this._struct.ref)); >>>>> But it seems that append does not work anyway. >>>>> * Finally, why I made the move to xml-datasource ? >>>>> (I thought) I could parse a local rdf file at init, and flushes the >>>>> modified results at destruction. I even could observe the download >>>>> progress from chrome (eg: with a progressbar), etc. >>>>> So any clue ? >>>>> What do you think is the best implementation ? >>>>> The first design could be perfect if i could override and extends >>>>> methods and interfaces of the __proto__ instance. >>>>> Thank you for reading me. >>>> I suspect that something in your implementation is broken. There ishttp://developer.mozilla.org/en/docs/Aggregating_the_In-Memory_Dataso..., >>>> but that hasn't been maintained in years, same goes for its sibling >>>> documents. >>> Yes, i know this document, and tried some months ago to agregate a xml- >>> datasource (for another project) and gave up when i saw that xml- >>> datasource cant be aggregated ;-) >>> Maybe i should give another try with the in-mem datasource. >>> But can you confirm me that i could try that with a javascript >>> implementation ? >>>> Sorry, no better idea, maybe if you post parts of your code there's >>>> better chances. >>> Héhé. The whole component is too long / time consuming for you to take >>> a look ? >>> I understand. >>> So first question, that i previously asked. WIth a component >>> implementation below, is it normal that I cant override public methods >>> of the __proto__. In fact, I can only add private method, that cant be >>> accessed by the chrome ? >>> function myComponentImplementation() { >>> } >>> myComponentImplementation.prototype { >>> __proto__ : Components.classes["@mozilla.org/rdf/datasource;1? >>> name=in-memory-datasource"] >>> .createInstance(Components.interfaces.nsIRDFDataSource), >>> QueryInterface : function(iid) { >>> if (!iid.equals(C_i.nsIDOMEventListener) && >>> !iid.equals(C_i.nsIRDFDataSource) && >>> !iid.equals(C_i.nsIRDFRemoteDataSource) && >>> !iid.equals(C_i.nsISupports)) >>> throw Components.results.NS_ERROR_NO_INTERFACE; >>> ConsoleService.logStringMessage("QI called:" + iid); >>> return this; >>> } >>> } >> That is more of a js/xpconnect question. Trying >> >> function A(){this.bar=3;} >> function B(){this.__proto__ = new A();this.foo=6}; >> b=new B() >> [object Object] >> props(b) >> Fields: foo >> Fields of prototype: bar >> Methods of prototype of prototype of prototype: extend >> b.bar >> 3 >> b.foo >> 6 >> >> makes me think that you should set the __proto__ to a new instance of >> the in-mem ds in the Constructor. Which makes sense, as you don't want >> to share the in-mem ds across all instances of >> myComponentImplementation, right? > > Good point. > > I just tried that : > > function myDataSourceImplementation(struct) > { > this.__proto__ = Components.classes["@mozilla.org/rdf/datasource;1? > name=in-memory-datasource"] > .createInstance(Components.interfaces.nsIRDFDataSource); > > this.init = function() { > // load data with XMLHttpRequest > }; > > this.QueryInterface = function(iid) { > ConsoleService.logStringMessage("QI called:" + iid); > if (!iid.equals(Components.interfaces.nsIDOMEventListener) && > !iid.equals(Components.interfaces.nsIRDFDataSource) && > !iid.equals(Components.interfaces.nsIRDFRemoteDataSource) > && > !iid.equals(Components.interfaces.nsISupports)) > throw Components.results.NS_ERROR_NO_INTERFACE; > return this; > }; > > this.init(); > > } > > As you can see, i'm compelled to binds new methods in the constructor > since, the prototype is overriden by "this.__proto__". Not a real > problem. > Code works (i see the data in the chrome). > > But my new QI is never called, like before, and prevents me from > implementing other interfaces :-( > >> But I guess for real questions on this, you should try .js-engine. > > Maybe. > This evening, I'll try XPCOM aggregation (like in "Aggregating In > Memory Datasource"), and will ask .js-engine for any clues. > I'll post back here any progress about this subject. > > Thanks for your time.