mozilla

Revision 165408 of User talk:sdwilsh

  • Revision slug: User_talk:sdwilsh
  • Revision title: User talk:sdwilsh
  • Revision id: 165408
  • Created:
  • Creator: Ancestor
  • Is current revision? No
  • Comment /* Discussion */

Revision Content

FUEL API for opening a window with parameters?

idl:

nsIDOMWindow open(in nsIDOMWindow aParent, in string aURL, in string aName, in string aFeatures, in unsigned long aParamCount, [array, size_is(aParamCount)] in aParams);

js to covert it for the window watcher

open: function FUEL_open(aParent, aURL, aName, aFeatures, aParamCount, aParams)
{
  var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);

  var args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
  for (var i = 0; i < aParams.length; i++) {
    var obj = null;

    switch (typeof(aParams[i])) {
      case "string":
        obj = Cc[@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
        obj.data = aParams[i];
        break;
      case "number":
        obj = Cc["@mozilla.org/supports-double;1"].createInstance(Ci.nsISupportsDouble);
        obj.data = aParams[i];
        break;
      default:
        // XXX some kind of error
    } 

    if (obj)
      args.appendElement(obj, false);
  }

  ww.open(aParent, aURL, aName, aFeatures, args);
}

Async Storage Statements

This is loosely based off of the HTML 5 SQL spec

Sample Code

This is an attempt at some sample code on using async statements.

JavaScript
// conn is a mozIStorageConnection
let stmt = conn.newAsyncTransaction();

// you should be able to bind numbered parameters (as well as unnumbered ones)
stmt.execute("SELECT * FROM moz_downloads WHERE state = ?1",
             [Ci.nsIDownloadManager.DOWNLOAD_FINISHED],
             {
               handleResult(aTransaction, aResult)
               {
                 for (let row in aResult) {
                   // process data
                   // should be able to access data in a row by name or argument number
                 }
               },
               handleError(aTransaction, aError)
               {
                 // update UI accordingly
               }
             });


// You should be able to bind parameters by name as well
stmt = conn.newAsyncTransaction();
stmt.execute("SELECT * FROM moz_downloads WHERE state = :state",
             {state: Ci.nsIDownloadManager.DOWNLOAD_FAILED},
             {
               handleResult(aTransaction, aResult)
               {
                 for (let row in aResult) {
                   // process data
                   // should be able to access data in a row by name or argument number
                 }
               },
               handleError(aTransaction, aError)
               {
                 // update UI accordingly
               }
             });
C++
class Listener : public AsyncTransactionLisener {
  NS_IMETHODIMP HandleResult(AsyncTransaction *aTransaction, ?? aResult)
  {
    nsCOMPtr<mozIStorageValueArray> row;
    while (NS_SUCCEEDED(aResult->GetRow(row)) && row) {
      // do something with result
    }
  }

  NS_IMETHODIMP HandleError(AsyncTransaction *aTransaction, ?? aError)
  {
  }
};

// conn is a mozIStorageConnection
nsCOMPtr<AsyncTransaction> stmt;
(void)conn->newAsyncTransaction(getter_AddRefs(stmt));

// Special object for C++ to bind params - implements some predefined interface
nsRefPtr<Parameters> params(new Parameters());
params->Bind(0, nsIDownloadManager::DOWNLOAD_FINISHED);

(void)stmt->Execute(
  NS_LITERAL_CSTRING("SELECT * FROM moz_downloads WHERE state = ?1"),
  params,
  new Listener()
);

// Also allows binding of named parameters
params = new Parameters();
parms->Bind(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
(void)stmt->Execute(
  NS_LITERAL_CSTRING("SELECT * FROM moz_downloads WHERE state = ?1"),
  params,
  new Listener()
);

Proposed Interface

This is a work in progress. Comments welcome.

mozIStorageConnection

These are necessary changes to the mozIStorageConnection interface.

AsyncTransaction newAsyncTransaction();

Creates a new object to execute the async transaction.

AsyncTransaction

This is a new interface that handles asynchronous calls to the db.

void execute(in AUTF8String aSQL, [optional] in Parameters aParameters, [optional] in Callback aCallback);

Executes a sql statement and calls the callback when a result is returned.

Parameters

Interface to handle the adding of properties. My expectation is that this just offers a series of getters for storage to call, and we'll have special objects implemented in each language. This still needs lots of defining, so feedback welcome.

Callback

Interface handles some sort of callback on the calling thread.

void handleResult(in AsyncTransaction aTransaction, in ResultSet aResultSet);

Handles a successful execution of the statement.

void handleError(in AsyncTransaction aTransaction, in ?? aError);

Handles an error of the statement.

ResultSet

Interface used to get the data back. In JS, this should be able to be used as an iterator.

mozIStorageValueArray getNextResult();

Obtains the next tuple of results. Null if no results available.

Discussion

I think we should make async API as similar to the synchronous one as possible. What's the rationale behind basing the it on HTML5 SQL? It will create a huge inconsistency, unless we also plan to overhaul the rest of mozStorage to match HTML5:

  • AsyncTransaction is a completely different animal than a traditional transaction.
  • mozStorage APIs revolve around statement objects. There's no explicit statement here.
  • Parameters are bound in a different way
  • Results are read in a different way
  • AsyncTransaction.execute() reads as if it was the transaction, that's is being executed. C.f. mozIStorageStatement.execute().

Another concern is the callback. Let's make it a single function. With an object, you've got to implement QI and the code that handles the statement is more scattered. And if you use many async statements, you make a switch and forward the callbacks to separate dedicated functions, anyway.

Here's my idea of the API, the example should be self-explanatory. Please note that I have no knowledge of the internals of sqlite and mozStorage, so I don't know how feasible this is.

// conn is a mozIStorageConnection
let sql = "SELECT * FROM moz_downloads WHERE state = ?1";
let stmt = conn.createStatement(sql);
stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_FINISHED);
stmt.executeAsync(callback);

function callback(aStatement, aResult, aError) {
    if (aError)
        // update UI accordingly
        
    for (let row in aResult) {
        // process data
        // should be able to access data in a row by name or argument number
    }
}

Ancestor 15:48, 9 May 2008 (PDT)

Revision Source

<h3 name="FUEL_API_for_opening_a_window_with_parameters.3F"> FUEL API for opening a window with parameters? </h3>
<p>idl:
</p>
<pre>nsIDOMWindow open(in nsIDOMWindow aParent, in string aURL, in string aName, in string aFeatures, in unsigned long aParamCount, [array, size_is(aParamCount)] in aParams);</pre>
<p>js to covert it for the window watcher
</p>
<pre>open: function FUEL_open(aParent, aURL, aName, aFeatures, aParamCount, aParams)
{
  var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);

  var args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
  for (var i = 0; i &lt; aParams.length; i++) {
    var obj = null;

    switch (typeof(aParams[i])) {
      case "string":
        obj = Cc[@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
        obj.data = aParams[i];
        break;
      case "number":
        obj = Cc["@mozilla.org/supports-double;1"].createInstance(Ci.nsISupportsDouble);
        obj.data = aParams[i];
        break;
      default:
        // XXX some kind of error
    } 

    if (obj)
      args.appendElement(obj, false);
  }

  ww.open(aParent, aURL, aName, aFeatures, args);
}</pre>
<h3 name="Async_Storage_Statements"> Async Storage Statements </h3>
<p>This is loosely based off of the <a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/#executing">HTML 5 SQL spec</a>
</p>
<h4 name="Sample_Code"> Sample Code </h4>
<p>This is an attempt at some sample code on using async statements.
</p>
<h5 name="JavaScript"> JavaScript </h5>
<pre>// conn is a mozIStorageConnection
let stmt = conn.newAsyncTransaction();

// you should be able to bind numbered parameters (as well as unnumbered ones)
stmt.execute("SELECT * FROM moz_downloads WHERE state = ?1",
             [Ci.nsIDownloadManager.DOWNLOAD_FINISHED],
             {
               handleResult(aTransaction, aResult)
               {
                 for (let row in aResult) {
                   // process data
                   // should be able to access data in a row by name or argument number
                 }
               },
               handleError(aTransaction, aError)
               {
                 // update UI accordingly
               }
             });


// You should be able to bind parameters by name as well
stmt = conn.newAsyncTransaction();
stmt.execute("SELECT * FROM moz_downloads WHERE state = :state",
             {state: Ci.nsIDownloadManager.DOWNLOAD_FAILED},
             {
               handleResult(aTransaction, aResult)
               {
                 for (let row in aResult) {
                   // process data
                   // should be able to access data in a row by name or argument number
                 }
               },
               handleError(aTransaction, aError)
               {
                 // update UI accordingly
               }
             });
</pre>
<h5 name="C.2B.2B"> C++ </h5>
<pre>class Listener : public AsyncTransactionLisener {
  NS_IMETHODIMP HandleResult(AsyncTransaction *aTransaction, ?? aResult)
  {
    nsCOMPtr&lt;mozIStorageValueArray&gt; row;
    while (NS_SUCCEEDED(aResult-&gt;GetRow(row)) &amp;&amp; row) {
      // do something with result
    }
  }

  NS_IMETHODIMP HandleError(AsyncTransaction *aTransaction, ?? aError)
  {
  }
};

// conn is a mozIStorageConnection
nsCOMPtr&lt;AsyncTransaction&gt; stmt;
(void)conn-&gt;newAsyncTransaction(getter_AddRefs(stmt));

// Special object for C++ to bind params - implements some predefined interface
nsRefPtr&lt;Parameters&gt; params(new Parameters());
params-&gt;Bind(0, nsIDownloadManager::DOWNLOAD_FINISHED);

(void)stmt-&gt;Execute(
  NS_LITERAL_CSTRING("SELECT * FROM moz_downloads WHERE state = ?1"),
  params,
  new Listener()
);

// Also allows binding of named parameters
params = new Parameters();
parms-&gt;Bind(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
(void)stmt-&gt;Execute(
  NS_LITERAL_CSTRING("SELECT * FROM moz_downloads WHERE state = ?1"),
  params,
  new Listener()
);
</pre>
<h4 name="Proposed_Interface"> Proposed Interface </h4>
<p>This is a work in progress.  Comments welcome.
</p>
<h5 name="mozIStorageConnection"> mozIStorageConnection </h5>
<p>These are necessary changes to the mozIStorageConnection interface.
</p><p><code><a href="#AsyncTransaction">AsyncTransaction</a> newAsyncTransaction();</code>
</p><p>Creates a new object to execute the async transaction.
</p>
<h5 name="AsyncTransaction"> AsyncTransaction </h5>
<p>This is a new interface that handles asynchronous calls to the db.
</p><p><code>void execute(in <a href="en/AUTF8String">AUTF8String</a> aSQL, <span class="plain">[optional]</span> in <a href="#Parameters">Parameters</a> aParameters, <span class="plain">[optional]</span> in <a href="#Callback">Callback</a> aCallback);</code>
</p><p>Executes a sql statement and calls the callback when a result is returned.
</p>
<h5 name="Parameters"> Parameters </h5>
<p>Interface to handle the adding of properties.  My expectation is that this just offers a series of getters for storage to call, and we'll have special objects implemented in each language.  This still needs lots of defining, so feedback welcome.
</p>
<h5 name="Callback"> Callback </h5>
<p>Interface handles some sort of callback on the calling thread.
</p><p><code>void handleResult(in <a href="#AsyncTransaction">AsyncTransaction</a> aTransaction, in <a href="#ResultSet">ResultSet</a> aResultSet);</code>
</p><p>Handles a successful execution of the statement.
</p><p><code>void handleError(in <a href="#AsyncTransaction">AsyncTransaction</a> aTransaction, in ?? aError);</code>
</p><p>Handles an error of the statement.
</p>
<h5 name="ResultSet"> ResultSet </h5>
<p>Interface used to get the data back.  In JS, this should be able to be used as an iterator.
</p><p><code><a href="en/MozIStorageValueArray">mozIStorageValueArray</a> getNextResult();</code>
</p><p>Obtains the next tuple of results.  Null if no results available.
</p>
<h4 name="Discussion"> Discussion </h4>
<p>I think we should make async API as similar to the synchronous one as possible. What's the rationale behind basing the it on HTML5 SQL? It will create a huge inconsistency, unless we also plan to overhaul the rest of mozStorage to match HTML5:
</p>
<ul><li> AsyncTransaction is a completely different animal than a traditional transaction.
</li><li> mozStorage APIs revolve around statement objects. There's no explicit statement here.
</li><li> Parameters are bound in a different way
</li><li> Results are read in a different way
</li><li> <code>AsyncTransaction.execute()</code> reads as if it was the transaction, that's is being executed. C.f. <code>mozIStorageStatement.execute()</code>.
</li></ul>
<p>Another concern is the callback. Let's make it a single function. With an object, you've got to implement QI and the code that handles the statement is more scattered. And if you use many async statements, you make a <code>switch</code> and forward the callbacks to separate dedicated functions, anyway.
</p><p>Here's my idea of the API, the example should be self-explanatory. Please note that I have no knowledge of the internals of sqlite and mozStorage, so I don't know how feasible this is.
</p>
<pre>// conn is a mozIStorageConnection
let sql = "SELECT * FROM moz_downloads WHERE state = ?1";
let stmt = conn.createStatement(sql);
stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_FINISHED);
stmt.executeAsync(callback);

function callback(aStatement, aResult, aError) {
    if (aError)
        // update UI accordingly
        
    for (let row in aResult) {
        // process data
        // should be able to access data in a row by name or argument number
    }
}
</pre>
<p><a href="User:Ancestor">Ancestor</a> 15:48, 9 May 2008 (PDT)
</p>
Revert to this revision