Search code examples
javascriptasynchronoussqlitefirefox-addonfirefox-addon-sdk

Getting result from querying sqlite db in the add-on script to be submitted to the content script


I am writting a modest firefox add-on and I have some problems getting the results used inside the "flow" of the add-on script.

I have the code taking care of querying a sqlite database as a module but I don't know how to create a callback inside of it so that the pagemod in the add-on script can use it and pass it to the content script.

Basically here is what I have:

  • main.js :
var pageMod = require("sdk/page-mod");
var self = require("sdk/self");
var myDbScript = require('./myDbScript');

pageMod.PageMod({
    include: "*.example.com/*",
    contentScriptFile:  [self.data.url('jquery-1.10.2.min.js'),
                        self.data.url('myContentScript.js')],
    onAttach: function(worker) {
        // Query the database on behalf of the content script
        worker.port.on('queryTheDB', function(message) {            
            // Get the data from the DB (é is some test value here)
            // Not working because asynchronous querying of the DB
            var resultFromDB = myDbScript.getResult(2);

            // Send the result to the content script
            worker.port.emit('hereIsYourResult', resultFromDB);
        });
    }
});
  • myDBScript.js
// Get required components
var {components} = require("chrome");
components.utils.import("resource://gre/modules/FileUtils.jsm");
components.utils.import("resource://gre/modules/Services.jsm");

// Some code to get the DB

// Create statement to retrieve country based on the IP
var statement = dbConnection.createStatement("SELECT col1, col2 FROM table WHERE col1 = :given_col1");

function getResult(submittedValue) {    
    // Bind parameters
    statement.params.given_col1 = submittedValue;   

    // Execute
    statement.executeAsync({
        handleResult: function(aResultSet) {
            for (let row = aResultSet.getNextRow();
                 row;
                 row = aResultSet.getNextRow()) {

            var resultFromDB = row.getResultByName("col2");
            }
        },

        handleError: function(aError) {
            print("Error: " + aError.message);
            return 'error';
        },

        handleCompletion: function(aReason) {
            if (aReason != components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
                print("Query canceled or aborted!");
                return 'canceledOrAborted';
            } else {
                // Sending the result to the add-on script so that it can
                // pass it to the content script
                notifyingTheAddonScript(resultFromDB);
            }
        }
    });
}

// Enable the use of the getResult function
exports.getResult = getResult;

The thing is that I don't see how to have the addon script be aware that the result is ready. Please bear with me, I am a noob at this...


Solution

  • Since I don't have the full source, I cannot test. So you'll have to fix any I made errors yourself ;)

    First, lets add a callback.

    // @param {function(result, error)} callback
    // Called upon query completion.
    // if |error| is a string, then the query failed.
    // Else |result| will contain an array of values. 
    function getResult(submittedValue, callback) { // changed   
      // Bind parameters
      statement.params.given_col1 = submittedValue;   
    
      var rv = [], err = null; // added
      // Execute
      statement.executeAsync({
        handleResult: function(aResultSet) {
          for (let row = aResultSet.getNextRow();
            row;
            row = aResultSet.getNextRow()) {
    
            rv.push(row.getResultByName("col2")); // changed
          }
        },
    
        handleError: function(aError) {
          print("Error: " + aError.message);
          err = aError.message; // changed
        },
    
        handleCompletion: function(aReason) {
          if (aReason != components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
            print("Query canceled or aborted!");
            err = err || 'canceled or aborted'; // changed
          }
          callback(err ? null : rv, err); // replaced
        }
      });
    }
    

    Lets use this stuff now in the pagemod

    onAttach: function(worker) {
      // Query the database on behalf of the content script
      worker.port.on('queryTheDB', function(message) {            
        // Get the data from the DB (é is some test value here)
        // Not working because asynchronous querying of the DB
        myDbScript.getResult(2, function callback(result, error) {
          if (error) {
            worker.port.emit("hereIsYourError", error);
            return;
          }
          worker.port.emit("hereIsYourResult", result);
        });
      });
    }
    

    You might want to take some precautions not to fire multiple queries. While it would be OK to do so, it might hurt performance ;)

    Since our callback already looks kinda like a promise, it might actually be a good idea to use promises, maybe even with the Sqlite.jsm module and some Task.jsm magic.