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 ?