home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / components / sbMetrics.js < prev    next >
Text File  |  2012-06-08  |  14KB  |  451 lines

  1. /**
  2. //
  3. // BEGIN SONGBIRD GPL
  4. // 
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2008 POTI, Inc.
  8. // http://songbirdnest.com
  9. // 
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the "GPL").
  12. // 
  13. // Software distributed under the License is distributed 
  14. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
  15. // express or implied. See the GPL for the specific language 
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this 
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc., 
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. // 
  23. // END SONGBIRD GPL
  24. //
  25.  */
  26. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  27.  
  28. const SONGBIRD_METRICS_CONTRACTID = "@songbirdnest.com/Songbird/Metrics;1";
  29. const SONGBIRD_METRICS_CLASSNAME = "Songbird Metrics Service Interface";
  30. const SONGBIRD_METRICS_CID = Components.ID("{1066527d-b135-4e0c-9ea4-f6109ae97d02}");
  31. const SONGBIRD_METRICS_IID = Components.interfaces.sbIMetrics;
  32.  
  33. const SONGBIRD_POSTMETRICS_PREFKEY = "songbird.url.metrics";
  34.  
  35. const SONGBIRD_UPLOAD_METRICS_EVERY_NDAYS = 1; // every day
  36.  
  37. function Metrics() {
  38.     this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
  39.                       .getService(Components.interfaces.nsIPrefBranch);
  40. }
  41.  
  42. Metrics.prototype = {
  43.   classDescription: SONGBIRD_METRICS_CLASSNAME,
  44.   classID:          SONGBIRD_METRICS_CID,
  45.   contractID:       SONGBIRD_METRICS_CONTRACTID,
  46.   QueryInterface:   XPCOMUtils.generateQI([
  47.     SONGBIRD_METRICS_IID,
  48.     Components.interfaces.nsIWebProgressListener,
  49.     Components.interfaces.nsISupportsWeakReference
  50.   ]),
  51.  
  52.   _postreq: null,
  53.   _dbquery: null,
  54.   
  55.   LOG: function(str) {
  56.     var consoleService = Components.classes['@mozilla.org/consoleservice;1']
  57.                             .getService(Components.interfaces.nsIConsoleService);
  58.     consoleService.logStringMessage(str);
  59.   },
  60.  
  61.  
  62.   /**
  63.    * Check to see if metrics should be submitted.
  64.    */
  65.   checkUploadMetrics: function()
  66.   {
  67.     if (!this._isEnabled()) return;
  68.     
  69.     var timeUp = this._isWaitPeriodUp();
  70.     
  71.     if (timeUp)
  72.     {
  73.       this.uploadMetrics();
  74.     } 
  75.   },
  76.   
  77.   /**
  78.    * Bundle all metrics info and send it to the server.
  79.    *
  80.    * TODO: Rethink version and OS strings
  81.    */
  82.   uploadMetrics: function()
  83.   {
  84.     var user_install_uuid = this._getPlayerUUID();
  85.     
  86.     var xulRuntime = Components.classes["@mozilla.org/xre/runtime;1"].getService(Components.interfaces.nsIXULRuntime);    
  87.     var user_os = xulRuntime.OS;
  88.  
  89.     
  90.     var metrics = this._getTable();
  91.  
  92.     var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
  93.     // appInfo.name + " " + appInfo.version + " - " + appInfo.appBuildID;    
  94.  
  95.     var abi = "Unknown";
  96.     // Not all builds have a known ABI
  97.     try {
  98.       abi = appInfo.XPCOMABI;
  99.       
  100.       // TODO: Throwing an exception every time is bad.. should probably detect os x
  101.       
  102.       // Mac universal build should report a different ABI than either macppc
  103.       // or mactel.
  104.       var macutils = Components.classes["@mozilla.org/xpcom/mac-utils;1"]
  105.                                .getService(Components.interfaces.nsIMacUtils); 
  106.       if (macutils.isUniversalBinary)  abi = "Universal-gcc3";
  107.     }
  108.     catch (e) {}
  109.  
  110.     var platform = appInfo.OS + "_" + abi;
  111.     
  112.     var tzo = (new Date()).getTimezoneOffset();
  113.     var neg = (tzo < 0);
  114.     tzo = Math.abs(tzo);
  115.     var tzh = Math.floor(tzo / 60);
  116.     var tzm = tzo - (tzh*60);
  117.     // note: timezone is -XX:XX if the offset is positive, and +XX:00 if the offset is negative !
  118.     // this is because the offset has the reverse sign compared to the timezone, since
  119.     // the offset is what you should add to localtime to get UTC, so if you add -XX:XX, you're
  120.     // subtracting XX:XX, because the timezone is UTC+XX:XX
  121.     var tz = (neg ? "+" : "-") + this.formatDigits(tzh,2) + ":" + this.formatDigits(tzm,2);
  122.     
  123.     // build xml
  124.     
  125.     var xml = "";
  126.     xml += '<metrics schema_version="2.0" guid="' + user_install_uuid 
  127.             + '" version="' + appInfo.version 
  128.             + '" build="' + appInfo.appBuildID
  129.             + '" product="' + appInfo.name
  130.             + '" platform="' + platform
  131.             + '" os="' + user_os 
  132.             + '" timezone="' + tz 
  133.             + '">\n';
  134.  
  135.     for (var i = 0; i < metrics.length; i++) 
  136.     {
  137.       var key = metrics[i][0];
  138.       var val = metrics[i][1];
  139.       if ( val > 0 )
  140.       {
  141.         var dot = key.indexOf(".");
  142.         if (dot >= 0) {
  143.           var timestamp = key.substr(0, dot);
  144.           var cleanKey = key.substr(dot + 1);
  145.           var date = new Date();
  146.           date.setTime(timestamp);
  147.           
  148.           var hourstart = date.getFullYear() + "-" + 
  149.                           this.formatDigits(date.getMonth()+1,2) + "-" + 
  150.                           this.formatDigits(date.getDate(),2) + " " + 
  151.                           this.formatDigits(date.getHours(),2) + ":" + 
  152.                           this.formatDigits(date.getMinutes(),2) + ":" + 
  153.                           this.formatDigits(date.getSeconds(),2);
  154.           xml += '\t<item hour_start="' + hourstart + '" key="' + encodeURIComponent(cleanKey) + '" value="' + val + '"/>\n';
  155.         }
  156.       }
  157.     }
  158.     xml += '</metrics>';
  159.  
  160. /*
  161.     // Happy little self-contained test display
  162.     var gPrompt = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  163.                           .getService(Components.interfaces.nsIPromptService);
  164.     gPrompt.alert( null, "METRICS XML", xml );
  165. */
  166.  
  167.     // upload xml
  168.  
  169.     var domparser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
  170.                       .getService(Components.interfaces.nsIDOMParser);   
  171.     var document = domparser.parseFromString(xml, "text/xml");
  172.  
  173.     var onpostload = { 
  174.       _that: null, 
  175.       handleEvent: function( event ) { this._that.onPostLoad(); } 
  176.     };
  177.     onpostload._that = this;
  178.     
  179.     var onposterror = { 
  180.       _that: null, 
  181.       handleEvent: function( event ) { this._that.onPostError(); } 
  182.     };
  183.     onposterror._that = this;
  184.  
  185.     var postURL = this.prefs.getCharPref(SONGBIRD_POSTMETRICS_PREFKEY);
  186.     
  187.     this._postreq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest); 
  188.     this._postreq.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest).addEventListener("load", onpostload, false);
  189.     this._postreq.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest).addEventListener("error", onposterror, false);
  190.     this._postreq.open('POST', postURL, true); 
  191.     this._postreq.send(document);
  192.   },
  193.   
  194.   formatDigits: function(str, n) {
  195.     str = str+'';
  196.     while (str.length < n) str = "0" + str;
  197.     return str;
  198.   },
  199.  
  200.   onPostLoad: function() {
  201.     this.LOG("POST metrics done: "  + this._postreq.status + " - " + this._postreq.responseText);
  202.     
  203.     // POST successful, reset all metrics to 0
  204.     if (this._postreq.status == 200 && this._postreq.responseText == "OK") 
  205.     {
  206.       this._emptyTable();
  207.       var pref = Components.classes["@mozilla.org/preferences-service;1"]
  208.                         .getService(Components.interfaces.nsIPrefBranch);
  209.       var timenow = new Date();
  210.       var now = timenow.getTime();
  211.       
  212.       pref.setCharPref("app.metrics.last_upload", now);
  213.       pref.setCharPref("app.metrics.last_version", this._getCurrentVersion());
  214.       pref.setIntPref("app.metrics.last_update_count", this._getUpdateCount());
  215.       
  216.       this.LOG("metrics reset");
  217.     }    
  218.     else 
  219.     {
  220.       this.LOG("POST metrics failed: " + this._postreq.responseText);
  221.     }
  222.   },
  223.  
  224.   onPostError: function() {
  225.     this.LOG("POST metrics error");
  226.   },
  227.   
  228.   
  229.   
  230.   /**
  231.    * Return true unless metrics have been 
  232.    * explicitly disabled.
  233.    */
  234.   _isEnabled: function() {
  235.   
  236.     // Make sure we are allowed to send metrics
  237.     var enabled = 0;
  238.     try {
  239.       enabled = parseInt(this.prefs.getCharPref("app.metrics.enabled"));
  240.     }
  241.     catch (e) { }
  242.     //if (!enabled) dump("*** METRICS ARE DISABLED ***\n");
  243.     
  244.     return enabled;
  245.   },
  246.   
  247.   
  248.   /**
  249.    * Return true if SONGBIRD_UPLOAD_METRICS_EVERY_NDAYS days have passed
  250.    * since last submission
  251.    */
  252.   _isWaitPeriodUp: function() { 
  253.                   
  254.     var timenow = new Date();
  255.     var now = timenow.getTime();
  256.     var last = 0;
  257.     try 
  258.     {
  259.       last = parseInt(this.prefs.getCharPref("app.metrics.last_upload"));
  260.     }
  261.     catch (e)
  262.     {
  263.       // first start, pretend we just uploaded so we'll trigger the next upload in n days
  264.       this.prefs.setCharPref("app.metrics.last_upload", now);
  265.       last = now;
  266.     }
  267.     
  268.     var diff = now - last;
  269.     
  270.     return (diff > (1000 /*one second*/ * 60 /*one minute*/ * 60 /*one hour*/ * 24 /*one day*/ * SONGBIRD_UPLOAD_METRICS_EVERY_NDAYS))
  271.   },
  272.  
  273.  
  274.   /**
  275.    * Has the version changed since last metrics submission
  276.    */
  277.   _hasVersionChanged: function() {
  278.   
  279.     var upgraded = false;
  280.     
  281.     var currentVersion = this._getCurrentVersion();
  282.     var lastVersion = null;
  283.     
  284.     try 
  285.     {
  286.       lastVersion = this.prefs.getCharPref("app.metrics.last_version");
  287.     }
  288.     catch (e) { }    
  289.     
  290.     if (currentVersion != lastVersion) 
  291.     {
  292.         upgraded = true;
  293.     }
  294.     
  295.     return upgraded;
  296.   },
  297.   
  298.   /**
  299.    * TODO: REPLACE WITH SOMETHING OFFICIAL
  300.    */  
  301.   _getCurrentVersion: function() {
  302.   
  303.     var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
  304.     return appInfo.name + " " + appInfo.version + " - " + appInfo.appBuildID;    
  305.   },
  306.   
  307.  
  308.   /**
  309.    * Find out how many updates have been applied through the update manager
  310.    */  
  311.   _getUpdateCount: function() {
  312.     var updateManager = Components.classes["@mozilla.org/updates/update-manager;1"].getService(Components.interfaces.nsIUpdateManager);
  313.     return updateManager.updateCount;    
  314.   },
  315.   
  316.   
  317.   /**
  318.    * TODO: REPLACE WITH SOMETHING OFFICIAL
  319.    */  
  320.   _getPlayerUUID: function() {
  321.   
  322.     var uuid = "";
  323.    
  324.     try 
  325.     {
  326.       uuid = this.prefs.getCharPref("app.player_uuid");
  327.     }
  328.     catch (e)
  329.     {
  330.       uuid = "";
  331.     }   
  332.     
  333.     if (uuid == "")
  334.     {
  335.         var aUUIDGenerator = Components.classes["@mozilla.org/uuid-generator;1"].createInstance(Components.interfaces.nsIUUIDGenerator);
  336.         uuid = aUUIDGenerator.generateUUID();
  337.         this.prefs.setCharPref("app.player_uuid", uuid);
  338.     }
  339.     
  340.     return uuid;     
  341.   },
  342.   
  343.   metricsInc: function( aCategory, aUniqueID, aExtraString ) {
  344.     this.metricsAdd( aCategory, aUniqueID, aExtraString, 1 );
  345.   },
  346.   
  347.   metricsAdd: function( aCategory, aUniqueID, aExtraString, aIntValue ) {
  348.     // timestamps are recorded as UTC !
  349.     var d = new Date();
  350.     var timestamp = (Math.floor(d.getTime() / 3600000) * 3600000) + (d.getTimezoneOffset() * 60000);
  351.  
  352.     // Cook up the key string
  353.     var key = timestamp + "." + aCategory + "." + aUniqueID;
  354.     if (aExtraString != null && aExtraString != "") key = key + "." + aExtraString;
  355.     
  356.     try {
  357.       // Don't record things if we're disabled.
  358.       if (!this._isEnabled()) return;
  359.  
  360.       // Make sure it's an actual int.      
  361.       intvalue = parseInt(aIntValue);
  362.     
  363.       // Then add our value to the old value and write it back
  364.       var cur = this._getValue( key );
  365.       var newval = cur + intvalue;
  366.       this._setValue( key, newval );
  367.     } 
  368.     catch(e) { 
  369.       this.LOG("error: metricsAdd( " + 
  370.                aCategory +
  371.                ", " +
  372.                aUniqueID + 
  373.                ", " + 
  374.                aExtraString + 
  375.                ", " + 
  376.                aIntValue + 
  377.                " ) == '" + 
  378.                key + 
  379.                "'\n\n" + 
  380.                e);
  381.     }
  382.   },
  383.   
  384.   _initDB: function() {
  385.     if (!this._dbquery) {
  386.       this._dbquery = Components.classes["@songbirdnest.com/Songbird/DatabaseQuery;1"].
  387.                                  createInstance(Components.interfaces.sbIDatabaseQuery);
  388.       this._dbquery.setAsyncQuery(false);
  389.       this._dbquery.setDatabaseGUID("metrics");
  390.       this._dbquery.resetQuery();
  391.       this._dbquery.addQuery("CREATE TABLE IF NOT EXISTS metrics (keyname TEXT UNIQUE NOT NULL, keyvalue BIGINT DEFAULT 0)");
  392.       this._dbquery.execute();
  393.     }
  394.   },
  395.   
  396.   _getValue: function(key) {
  397.     var retval = 0;
  398.  
  399.     this._initDB();
  400.     this._dbquery.resetQuery();
  401.   
  402.     this._dbquery.addQuery("SELECT * FROM metrics WHERE keyname = \"" + key + "\"");
  403.     this._dbquery.execute();
  404.  
  405.     var dbresult = this._dbquery.getResultObject();
  406.     if (dbresult.getRowCount() > 0) {
  407.       retval = parseInt(dbresult.getRowCell(0, 1));
  408.     }
  409.  
  410.     return retval;
  411.   },
  412.    
  413.   _setValue: function(key, n) {
  414.     this._initDB();
  415.     this._dbquery.resetQuery();
  416.     this._dbquery.addQuery("INSERT OR REPLACE INTO metrics VALUES (\"" + key + "\", " + n + ")");
  417.     this._dbquery.execute();
  418.   },
  419.   
  420.   _getTable: function() {
  421.     var table = new Array();
  422.     this._initDB();
  423.     this._dbquery.resetQuery();
  424.     this._dbquery.addQuery("SELECT * FROM metrics");
  425.     this._dbquery.execute();
  426.  
  427.     var dbresult = this._dbquery.getResultObject();
  428.     var count = dbresult.getRowCount();
  429.  
  430.     for (var i=0;i<count;i++) {
  431.       var key = dbresult.getRowCell(i, 0);
  432.       var val = parseInt(dbresult.getRowCell(i, 1));
  433.       table.push([key, val]);
  434.     }
  435.     
  436.     return table;
  437.   },
  438.   
  439.   _emptyTable: function() {
  440.     this._initDB();
  441.     this._dbquery.resetQuery();
  442.     this._dbquery.addQuery("DELETE FROM metrics");
  443.     this._dbquery.execute();
  444.   },
  445. } // Metrics.prototype
  446.  
  447. function NSGetModule(compMgr, fileSpec) {
  448.   return XPCOMUtils.generateModule([Metrics]);
  449. }
  450.  
  451.