Re: this.__proto__ = Ci[].createInstance and QueryInterface calling problem
mcben <[email protected]> Mon, 04 Feb 2008 19:13:27 +0100
| Newsgroups | gmane.comp.mozilla.devel.rdf |
|---|---|
| Message-ID | <[email protected]> |
Axel Hecht a écrit : > 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 Thank you Axel. And (far.. far..) below is my last reply (that i sent to .rdf before the X-Post) with XPCOM aggregation with Javascript tries. > > 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. In fact, I already try to aggregate an in-memory datasource, that was not with xml datasource. See here my post (without replies :[) to .xpcom: http://groups.google.com/group/mozilla.dev.tech.xpcom/browse_frm/thread/38aeabcf8bfef28c/6d12b03fa5010e7b?lnk=st&q=Javascript%2C+RDF+in-mem+datasource+and+XPCOM+Aggregation#6d12b03fa5010e7b My new tries is same as previous ones of today : in the chrome, i cant QI to another interface the datasource that i get from RDFService.GetDataSource(), nor if i directly call my component with Components.classes. The asked interface is not forwarded to the outter object, eg: my own component. Here is the code: function MyDataSourceImplementation(struct) { this.mInner = Components .classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] .createInstance(Components.interfaces.nsIRDFDataSource, this, Components.interfaces.nsISupports); } MyDataSourceImplementation.prototype = { mInner:null, QueryInterface : function MDSI_QueryInterface(iid) { if(iid.equals(Components.interfaces.nsIRDFDataSource)) return this.mInner; if (iid.equals(Components.interfaces.nsIDOMEventListener) || iid.equals(Components.interfaces.nsIRDFRemoteDataSource) || iid.equals(Components.interfaces.nsISupports)) return this; throw Components.results.NS_ERROR_NO_INTERFACE; } } It'worse, if I replace the mInner initialization with : this.mInner = Components .manager .createInstance(inMemDsCID, this, Components.interfaces.nsISupports); The datasource if filled, that's fine, but with some QI errors, but crashses with a Segmentation fault 15 seconde later. The QI also does not work. I can't find any XPCOM aggregation with javascript example :( Are my parameters right for createInstance ?