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

  1. /*
  2.  *=BEGIN SONGBIRD GPL
  3.  *
  4.  * This file is part of the Songbird web player.
  5.  *
  6.  * Copyright(c) 2005-2010 POTI, Inc.
  7.  * http://www.songbirdnest.com
  8.  *
  9.  * This file may be licensed under the terms of of the
  10.  * GNU General Public License Version 2 (the ``GPL'').
  11.  *
  12.  * Software distributed under the License is distributed
  13.  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
  14.  * express or implied. See the GPL for the specific language
  15.  * governing rights and limitations.
  16.  *
  17.  * You should have received a copy of the GPL along with this
  18.  * program. If not, go to http://www.gnu.org/licenses/gpl.html
  19.  * or write to the Free Software Foundation, Inc.,
  20.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21.  *
  22.  *=END SONGBIRD GPL
  23.  */
  24.  
  25. const Cc = Components.classes;
  26. const Ci = Components.interfaces;
  27. const Cu = Components.utils;
  28. const Cr = Components.results;
  29.  
  30. const SB_APPSTARTUPSERVICE_CLASSNAME  = "sbAppStartupService";
  31. const SB_APPSTARTUPSERVICE_DESC       = "Songbird Application Startup Service";
  32. const SB_APPSTARTUPSERVICE_CONTRACTID = "@songbirdnest.com/app-startup-service;1";
  33. const SB_APPSTARTUPSERVICE_CID        = "{bf8300d3-95a7-4f52-b8e1-7012d8c639b8}";
  34.  
  35. const COMMAND_LINE_TOPIC  = "sb-command-line-startup";
  36. const SHUTDOWN_TOPIC      = "quit-application";
  37.  
  38. const FUEL_CONTRACTID = "@mozilla.org/fuel/application;1";
  39. const DATAREMOTE_TESTMODE = "__testmode__";
  40.  
  41. const TEST_FLAG = "test";
  42.  
  43. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  44. Cu.import("resource://app/jsmodules/LocalStore.jsm");
  45. Cu.import("resource://app/jsmodules/SBDataRemoteUtils.jsm");
  46. Cu.import("resource://app/jsmodules/StringUtils.jsm");
  47. Cu.import("resource://app/jsmodules/WindowUtils.jsm");
  48.  
  49. /**
  50.  * Global getter for FUEL
  51.  */
  52. __defineGetter__("Application", function() {
  53.   delete Application;
  54.   Application = Cc["@mozilla.org/fuel/application;1"]
  55.                   .getService(Ci.fuelIApplication);
  56.   return Application;
  57. });
  58.  
  59. /**
  60.  * Global getter for Observer Service
  61.  */
  62. __defineGetter__("ObserverService", function() {
  63.   delete ObserverService;
  64.   ObserverService = Cc["@mozilla.org/observer-service;1"]
  65.                       .getService(Ci.nsIObserverService);
  66.   return ObserverService;
  67. });
  68.  
  69. function sbAppStartupService() {
  70.   // We need to look at the command line asap so we can determine
  71.   // if we are in test mode or not.
  72.   ObserverService.addObserver(this, COMMAND_LINE_TOPIC, false);
  73. }
  74.  
  75. sbAppStartupService.prototype =
  76. {
  77.   // Application is initialized
  78.   _initialized: false,
  79.   // Application is in test mode
  80.   _testMode: false,
  81.   // Application is shutdown
  82.   _shutdownComplete: false,
  83.   // Application restart is required
  84.   _restartRequired: false,
  85.   // DataRemote Command Line Handler
  86.   _dataRemoteCmdLineHandler: null,
  87.   // DataRemote For App Restart
  88.   _dataRemoteAppRestart: null,
  89.  
  90.   // nsIObserver
  91.   observe: function(aSubject, aTopic, aData) {
  92.     switch(aTopic) {
  93.       case COMMAND_LINE_TOPIC: {
  94.         // Done with this notification
  95.         ObserverService.removeObserver(this, COMMAND_LINE_TOPIC);
  96.  
  97.         // We need to catch quit-application to clean up
  98.         ObserverService.addObserver(this, SHUTDOWN_TOPIC, false);
  99.  
  100.         // Check to see if we are running in test mode.
  101.         this._checkForTestCommandLine(aSubject);
  102.  
  103.         // Bootstrap Application
  104.         this._bootstrap();
  105.       }
  106.       break;
  107.  
  108.       case SHUTDOWN_TOPIC: {
  109.         ObserverService.removeObserver(this, SHUTDOWN_TOPIC);
  110.  
  111.         // Shut it all down!
  112.         this._shutdown();
  113.       }
  114.       break;
  115.     }
  116.   },
  117.  
  118.   ///////////////////////////////////////////////////////////////////
  119.   // Internal Methods
  120.   ///////////////////////////////////////////////////////////////////
  121.  
  122.   _bootstrap: function() {
  123.     if (this._initialized) {
  124.       Cu.reportError("bootstrap is getting called multiple times");
  125.       return;
  126.     }
  127.  
  128.     // Show the first-run wizard
  129.     if (this._firstRun()) {
  130.       this._mainWindowStart();
  131.     }
  132.  
  133.     this._initApplication();
  134.     this._initRestarter();
  135.  
  136.     this._initialized = true;
  137.  
  138.     // Restart app if required.  Do this after initializing so that
  139.     // shutting down does not cause errors.
  140.     if (this._restartRequired) {
  141.       WindowUtils.restartApp();
  142.       return;
  143.     }
  144.  
  145.     // Check to see if any version update migrations need to be performed
  146.     this._migrator.doMigrations();
  147.   },
  148.  
  149.   _shutdown: function() {
  150.     if(this._shutdownComplete) {
  151.       Cu.reportError("shutdown is getting called multiple times");
  152.       return;
  153.     }
  154.  
  155.     this._shutdownApplication();
  156.     this._cleanupRestarter();
  157.  
  158.     if(this._testMode) {
  159.       SBDataSetBoolValue(DATAREMOTE_TESTMODE, false);
  160.     }
  161.   },
  162.  
  163.   _checkForTestCommandLine: function(aCommandLine) {
  164.     try {
  165.       var cmdLine = aCommandLine.QueryInterface(Ci.nsICommandLine);
  166.       var flagPos = cmdLine.findFlag(TEST_FLAG, false);
  167.  
  168.       if(flagPos >= 0) {
  169.         this._testMode = true;
  170.         SBDataSetBoolValue(DATAREMOTE_TESTMODE, true);
  171.       }
  172.       else {
  173.         // Explicitily reset testmode if we don't see the test flag.
  174.         // We do this in case a previous test run didn't shut down
  175.         // cleanly so that a developer isn't confused about the main
  176.         // window not coming up.
  177.         SBDataSetBoolValue(DATAREMOTE_TESTMODE, false);
  178.       }
  179.     }
  180.     catch(e) {
  181.       Cu.reportError(e);
  182.     }
  183.   },
  184.  
  185.   /**
  186.    * \brief Main Application Startup
  187.    */
  188.   _initApplication: function () {
  189.     try {
  190.       // Initialize the extension manager and remote api whitelists.
  191.       this._initExtensionManagerPermissions();
  192.       this._initRemoteAPIPermissions();
  193.     }
  194.     catch (e) {
  195.       Cu.reportError(e);
  196.     }
  197.  
  198.     try {
  199.       // Startup the Metrics
  200.       this._metricsAppStart();
  201.     }
  202.     catch (e) {
  203.       Cu.reportError(e);
  204.     }
  205.  
  206.     try {
  207.       // Handle dataremote commandline parameters
  208.       this._initDataRemoteCmdLine();
  209.     }
  210.     catch (e) {
  211.       Cu.reportError(e);
  212.     }
  213.  
  214.     try {
  215.       // Handle app startup command line parameters
  216.       this._initStartupCmdLine();
  217.     }
  218.     catch( e ) {
  219.       Cu.reportError(e);
  220.     }
  221.  
  222.     try {
  223.       // On Windows and Linux, register the songbird:// protocol.
  224.       // (Mac is in the info.plist)
  225.       var platform = Cc["@mozilla.org/system-info;1"]
  226.                        .getService(Ci.nsIPropertyBag2)
  227.                        .getProperty("name");
  228.       if (platform == "Windows_NT") {
  229.         this._registerProtocolHandlerWindows();
  230.       }
  231.       else {
  232.         this._registerProtocolHandlerGConf();
  233.       }
  234.     }
  235.     catch (e) {
  236.       Cu.reportError(e);
  237.     }
  238.   },
  239.  
  240.   /**
  241.    * \brief Main Application Shutdown
  242.    */
  243.   _shutdownApplication: function () {
  244.     // Shutdown app startup commandline handler
  245.     this._cleanupStartupCmdLine();
  246.  
  247.     // Shutdown dataremote commandline handler
  248.     this._cleanupDataRemoteCmdLine();
  249.  
  250.     // Shutdown the Metrics
  251.     this._metricsAppShutdown();
  252.   },
  253.  
  254.   /**
  255.    * \brief Main Application Window Startup
  256.    */
  257.   _mainWindowStart: function () {
  258.     try {
  259.       var metrics = Cc["@songbirdnest.com/Songbird/Metrics;1"]
  260.                       .createInstance(Ci.sbIMetrics);
  261.       metrics.checkUploadMetrics();
  262.     }
  263.     catch (e) {
  264.       Cu.reportError(e);
  265.     }
  266.  
  267.     // Check for recommended add-on bundle updates.  This may wait for recommended
  268.     // add-on bundle download or the recommended add-on wizard.
  269.     try {
  270.       // Check for updates.
  271.       var addOnBundleUpdateService =
  272.             Cc["@songbirdnest.com/AddOnBundleUpdateService;1"]
  273.               .getService(Ci.sbIAddOnBundleUpdateService);
  274.       addOnBundleUpdateService.checkForUpdates();
  275.  
  276.       // If restart is required, mark application for restart and return.  Don't
  277.       // restart here, because doing so will lead to errors in shutting down.
  278.       if (addOnBundleUpdateService.restartRequired) {
  279.         this._restartRequired = true;
  280.         return;
  281.       }
  282.     } catch (e) {
  283.       Cu.reportError(e);
  284.     }
  285.  
  286.     // Make sure the web playlist is enabled.
  287.     // This is to protect against cases where the app is shut down
  288.     // while an extension has the web playlist disabled.
  289.     SBDataSetBoolValue("webplaylist.enabled", true);
  290.  
  291.     var feathersManager = Cc['@songbirdnest.com/songbird/feathersmanager;1']
  292.                             .getService(Ci.sbIFeathersManager);
  293.     feathersManager.openPlayerWindow();
  294.   },
  295.  
  296.   /**
  297.    * \brief Application First Run
  298.    */
  299.   _firstRun: function () {
  300.     var perfTest = false;
  301.  
  302.     // Skip first-run if we are in test mode (ie. started with -test)
  303.     if (this._testMode) {
  304.       return true;
  305.     }
  306.  
  307.     // Skip first-run if allowed (debug mode) and set to skip
  308.     if (Application.prefs.getValue("songbird.first_run.allow_skip", false)) {
  309.       var environment = Cc["@mozilla.org/process/environment;1"]
  310.                           .getService(Ci.nsIEnvironment);
  311.  
  312.       // If we're skipping the first run
  313.       if (environment.exists("SONGBIRD_SKIP_FIRST_RUN")) {
  314.         // Check to see if we're skipping all or just part
  315.         var skipFirstRun = environment.get("SONGBIRD_SKIP_FIRST_RUN");
  316.         perfTest = (skipFirstRun == "CSPerf");
  317.  
  318.         // Skip the first run wizard unless we're in a perf run.
  319.         if (skipFirstRun && !perfTest)
  320.           return true;
  321.       }
  322.     }
  323.  
  324.     try {
  325.       var haveRun = Application.prefs.getValue("songbird.firstrun.check.0.3",
  326.                                                false);
  327.       if (!haveRun) {
  328.  
  329.         var self = this;
  330.         var data = {
  331.           perfTest: null,
  332.           onComplete: function(aRedo) {
  333.             self._firstRunComplete(aRedo);
  334.           },
  335.           QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
  336.         };
  337.  
  338.         data.perfTest = perfTest;
  339.  
  340.         // Bah, we need to wrap the object so it crosses
  341.         // xpconnect boundaries properly.
  342.         var sip = Cc["@mozilla.org/supports-interface-pointer;1"]
  343.                     .createInstance(Ci.nsISupportsInterfacePointer);
  344.         sip.data = data;
  345.  
  346.         // Wrap it.
  347.         data.wrappedJSObject = data;
  348.  
  349.         // This cannot be modal as it will block the download of extensions.
  350.         WindowUtils.openDialog(null,
  351.                                "chrome://songbird/content/xul/firstRunWizard.xul",
  352.                                "firstrun",
  353.                                "chrome,titlebar,centerscreen,resizable=no",
  354.                                false,
  355.                                [sip]);
  356.         // Avoid leaks
  357.         sip.data = null;
  358.  
  359.         // Do not open main window until the non-modal first run dialog returns
  360.         return false;
  361.       }
  362.     } catch (e) {
  363.       Cu.reportError(e);
  364.     }
  365.  
  366.     // If we reach this point this is not the first run and the user has accepted
  367.     //   the EULA so launch the main window.
  368.     return true;
  369.   },
  370.  
  371.   _firstRunComplete: function(aRedo) {
  372.     if (aRedo) {
  373.       this._firstRun();
  374.     }
  375.     else {
  376.       // If EULA has not been accepted, quit application.
  377.       var eulaAccepted =
  378.         Application.prefs.getValue("songbird.eulacheck", false);
  379.  
  380.       if (!eulaAccepted) {
  381.         this._shutdown();
  382.         var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
  383.                                    .getService(Components.interfaces.nsIAppStartup);
  384.         appStartup.quit(appStartup.eForceQuit);
  385.       }
  386.       else {
  387.         this._mainWindowStart();
  388.       }
  389.     }
  390.   },
  391.  
  392.   /**
  393.    * \brief Initialize the Application Restarter. The restarter is used
  394.    *        during the first run process to restart the application
  395.    *        after first-run add-ons have been installed.
  396.    */
  397.   _initRestarter: function () {
  398.     this._dataRemoteAppRestart = SBNewDataRemote( "restart.restartnow", null );
  399.     this._dataRemoteAppRestart.boolValue = false;
  400.  
  401.     this._dataRemoteAppRestartHandler = {
  402.       observe: function ( aSubject, aTopic, aData ) {
  403.         var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
  404.                                    .getService(Components.interfaces.nsIAppStartup);
  405.         appStartup.quit(appStartup.eAttemptQuit | appStartup.eRestart);
  406.       }
  407.     };
  408.  
  409.     this._dataRemoteAppRestart.bindObserver( this._dataRemoteAppRestartHandler,
  410.                                              true );
  411.   },
  412.  
  413.   /**
  414.    * \brief Cleanup the Application Restarter.
  415.    */
  416.   _cleanupRestarter: function () {
  417.     this._dataRemoteAppRestart.unbind();
  418.     this._dataRemoteAppRestart = null;
  419.   },
  420.  
  421.   /**
  422.    * \brief Initialize the DataRemote used to propagate the command line
  423.    *        parameters.
  424.    */
  425.   _initDataRemoteCmdLine: function () {
  426.     var cmdLine = Cc["@songbirdnest.com/commandlinehandler/general-startup;1?type=songbird"];
  427.     if (!cmdLine)
  428.       return;
  429.  
  430.     var cmdLineService = cmdLine.getService(Ci.nsICommandLineHandler);
  431.     if (!cmdLineService)
  432.       return;
  433.  
  434.     var cmdLineManager =
  435.       cmdLineService.QueryInterface(Ci.sbICommandLineManager);
  436.     if (!cmdLineManager)
  437.       return;
  438.  
  439.     this._dataRemoteCmdLineHandler = {
  440.       handleFlag: function(aFlag, aParam)
  441.       {
  442.         var v = aParam.split("=");
  443.         if (v[0] && v[1]) {
  444.           SBDataSetStringValue(v[0], v[1]);
  445.           return true;
  446.         }
  447.  
  448.         return false;
  449.       },
  450.  
  451.       QueryInterface: XPCOMUtils.generateQI([Ci.sbICommandLineFlagHandler])
  452.     };
  453.  
  454.     cmdLineManager.addFlagHandler(this._dataRemoteCmdLineHandler, "data");
  455.   },
  456.  
  457.   /**
  458.    * \brief Cleanup the DataRemote used to propagate the command line
  459.    *        parameters.
  460.    */
  461.   _cleanupDataRemoteCmdLine: function () {
  462.     var cmdLine = Cc["@songbirdnest.com/commandlinehandler/general-startup;1?type=songbird"];
  463.     if (!cmdLine)
  464.       return;
  465.  
  466.     var cmdLineService = cmdLine.getService(Ci.nsICommandLineHandler);
  467.     if (!cmdLineService)
  468.       return;
  469.  
  470.     var cmdLineManager =
  471.       cmdLineService.QueryInterface(Ci.sbICommandLineManager);
  472.     if (!cmdLineManager)
  473.       return;
  474.  
  475.     cmdLineManager.removeFlagHandler(this._dataRemoteCmdLineHandler, "data");
  476.   },
  477.  
  478.   /**
  479.    * \brief Initialize the startup command line parameters.
  480.    */
  481.   _initStartupCmdLine: function () {
  482.     var cmdLine = Cc
  483.       ["@songbirdnest.com/commandlinehandler/general-startup;1?type=songbird"];
  484.     if (!cmdLine)
  485.       return;
  486.  
  487.     var cmdLineService = cmdLine.getService(Ci.nsICommandLineHandler);
  488.     if (!cmdLineService)
  489.       return;
  490.  
  491.     var cmdLineManager =
  492.       cmdLineService.QueryInterface(Ci.sbICommandLineManager);
  493.     if (!cmdLineManager)
  494.       return;
  495.  
  496.     // Add the start in application directory command line flag handler.
  497.     cmdLineManager.addFlagHandler(startupCmdLineHandler,
  498.                                   "start-in-app-directory");
  499.   },
  500.  
  501.   /**
  502.    * \brief Cleanup the startup command line parameters.
  503.    */
  504.   _cleanupStartupCmdLine: function () {
  505.     var cmdLine = Cc
  506.       ["@songbirdnest.com/commandlinehandler/general-startup;1?type=songbird"];
  507.     if (!cmdLine)
  508.       return;
  509.  
  510.     var cmdLineService = cmdLine.getService(Ci.nsICommandLineHandler);
  511.     if (!cmdLineService)
  512.       return;
  513.  
  514.     var cmdLineManager =
  515.       cmdLineService.QueryInterface(Ci.sbICommandLineManager);
  516.     if (!cmdLineManager)
  517.       return;
  518.  
  519.     // Remove the start in application directory command line flag handler.
  520.     cmdLineManager.removeFlagHandler(startupCmdLineHandler,
  521.                                      "start-in-app-directory");
  522.   },
  523.  
  524.   /**
  525.    * \brief Register the songbird:// protocol handler on Windows
  526.    */
  527.   _registerProtocolHandlerWindows: function () {
  528.     if (!("@mozilla.org/windows-registry-key;1" in Components.classes)) {
  529.       // no windows registry key, probably not Windows
  530.       return;
  531.     }
  532.  
  533.     var path = Cc["@mozilla.org/file/directory_service;1"]
  534.                  .getService(Ci.nsIProperties)
  535.                  .get("CurProcD", Ci.nsIFile);
  536.     var file = path.clone();
  537.     // It'd be nice if there were a way to look this up.
  538.     // (mook suggests) ::GetModuleFileNameW().
  539.     // http://mxr.mozilla.org/seamonkey/source/browser/components/shell/src/nsWindowsShellService.cpp#264
  540.     file.append("songbird.exe");
  541.  
  542.     var wrk = Cc["@mozilla.org/windows-registry-key;1"]
  543.                 .createInstance(Ci.nsIWindowsRegKey);
  544.  
  545.     wrk.create(wrk.ROOT_KEY_CURRENT_USER,
  546.                "Software\\Classes\\songbird",
  547.                wrk.ACCESS_WRITE);
  548.     wrk.writeStringValue("", "URL:Songbird protocol");
  549.     wrk.writeStringValue("URL Protocol", "");
  550.  
  551.     // The EditFlags key will make the protocol show up in the File Types
  552.     // dialog in Windows XP.
  553.     wrk.writeBinaryValue("EditFlags", '\002\000\000\000');
  554.  
  555.     wrk.create(wrk.ROOT_KEY_CURRENT_USER,
  556.                "Software\\Classes\\songbird\\DefaultIcon",
  557.                wrk.ACCESS_WRITE);
  558.     wrk.writeStringValue("", '"' + file.path + '"');
  559.  
  560.     wrk.create(wrk.ROOT_KEY_CURRENT_USER,
  561.                "Software\\Classes\\songbird\\shell\\open\\command",
  562.                wrk.ACCESS_WRITE);
  563.     wrk.writeStringValue("", '"' + file.path + '" "%1"');
  564.     wrk.close();
  565.   },
  566.  
  567.   /**
  568.    * \brief Register the songbird:// protocol handler on Gnome/Linux
  569.    */
  570.   _registerProtocolHandlerGConf: function () {
  571.     if (!("@mozilla.org/gnome-gconf-service;1" in Components.classes)) {
  572.       // gnome-gconf-service doesn't exist
  573.       return;
  574.     }
  575.     var path = Cc["@mozilla.org/file/directory_service;1"]
  576.                  .getService(Ci.nsIProperties)
  577.                  .get("CurProcD", Ci.nsIFile);
  578.     var file = path.clone();
  579.     file.append("songbird");
  580.  
  581.     var gconf = Cc["@mozilla.org/gnome-gconf-service;1"]
  582.                   .getService(Ci.nsIGConfService);
  583.     gconf.setString("/desktop/gnome/url-handlers/songbird/command", '"' + file.path + '" "%s"');
  584.     gconf.setBool("/desktop/gnome/url-handlers/songbird/enabled", true);
  585.     gconf.setBool("/desktop/gnome/url-handlers/songbird/needs_terminal", false);
  586.   },
  587.  
  588.   /**
  589.    * \brief Metrics Startup Cleanup
  590.    */
  591.   _metricsAppStart: function () {
  592.     var metrics = Cc["@songbirdnest.com/Songbird/Metrics;1"]
  593.                     .createInstance(Ci.sbIMetrics);
  594.     metrics.metricsInc("app", "appstart", null);
  595.  
  596.     // saw some strange issues with using the shortcut get/set versions of our
  597.     //   dataremote global methods so actually creating the dataremotes.
  598.     var reportedCrash = SBNewDataRemote( "crashlog.reported", null );
  599.     var dirtyExit = SBNewDataRemote( "crashlog.dirtyExit", null );
  600.     if ( dirtyExit.boolValue ) {
  601.       // we are reporting the crash, save that knowledge
  602.       reportedCrash.boolValue = true;
  603.  
  604.       var crashNum = SBNewDataRemote( "crashlog.num", null );
  605.       var uptime = SBNewDataRemote( "crashlog.uptime", null );
  606.  
  607.       // send the uptime for this past crash
  608.       // metrics key = crashlog.app.# = minute.
  609.       // The thinking here is that we may want to report on different kinds of
  610.       // crashes in the future. Therefore we will keep the top level bucket as
  611.       // crashlog, with a subdivision for the app.
  612.       metrics.metricsAdd("crashlog",
  613.                          "app" ,
  614.                          crashNum.intValue,
  615.                          uptime.intValue);
  616.  
  617.       // reset the uptime, inc the crashNum
  618.       uptime.intValue = 0;
  619.       crashNum.intValue = crashNum.intValue+1
  620.     } else {
  621.       // keep track of if we are reporting the crash, may be useful later
  622.       reportedCrash.boolValue = false;
  623.     }
  624.  
  625.     // mark this true so if we crash we know it. It gets reset by AppShutdown
  626.     dirtyExit.boolValue = true;
  627.  
  628.     var startstamp = (new Date()).getTime();
  629.     SBDataSetStringValue("startup_timestamp", startstamp); // 64bit, use StringValue
  630.  
  631.     // force prefs to write out
  632.     //  - too bad Application.prefs doesn't expose this method.
  633.     var prefService =
  634.       Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
  635.     prefService.savePrefFile(null);
  636.   },
  637.  
  638.   /**
  639.    * \brief Metrics Shutdown Cleanup
  640.    */
  641.   _metricsAppShutdown: function () {
  642.     var startstamp = parseInt( SBDataGetStringValue("startup_timestamp") ); // 64bit, use StringValue
  643.     var timenow = (new Date()).getTime();
  644.     var ticsPerMinute = 1000 * 60;
  645.     var minutes = ( (timenow - startstamp) / ticsPerMinute ) + 1; // Add one for fractionals
  646.  
  647.     var metrics = Cc["@songbirdnest.com/Songbird/Metrics;1"]
  648.                     .createInstance(Ci.sbIMetrics);
  649.     metrics.metricsAdd("app", "timerun", null, minutes);
  650.  
  651.     // we shut down cleanly, let AppStart know that -- for some reason
  652.     // SBDataSetBoolValue didn't work here, create the dataremote entirely.
  653.     var dirtyExit = SBNewDataRemote( "crashlog.dirtyExit", null );
  654.     dirtyExit.boolValue = false;
  655.  
  656.     // increment uptime since last crash
  657.     var uptime = SBNewDataRemote( "crashlog.uptime", null );
  658.     uptime.intValue = uptime.intValue + minutes;
  659.   },
  660.  
  661.   /**
  662.    * \brief Initialize the Extension Manager Permissions so it includes
  663.    *        the Songbird Add-Ons site as a permitted install location.
  664.    */
  665.   _initExtensionManagerPermissions: function () {
  666.     const httpPrefix = "http://";
  667.     const prefRoot = "xpinstall.whitelist.add";
  668.     const permissionType = "install";
  669.  
  670.     var prefBranch =
  671.       Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  672.  
  673.     var ioService =
  674.       Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  675.  
  676.     var permissionManager =
  677.       Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
  678.  
  679.     var childBranches = prefBranch.getChildList(prefRoot, {});
  680.  
  681.     for each (let prefName in childBranches) {
  682.       if(prefBranch.getPrefType(prefName) == Ci.nsIPrefBranch.PREF_STRING) {
  683.         let prefValue = prefBranch.getCharPref(prefName);
  684.         let values = prefValue.split(",");
  685.         for each (let value in values) {
  686.           if (value.length > 0) {
  687.             value = value.replace(" ", "", "g");
  688.             value = httpPrefix + value;
  689.             let uri = null;
  690.             try {
  691.               uri = ioService.newURI(value, null, null);
  692.               permissionManager.add(uri,
  693.                                     permissionType,
  694.                                     Ci.nsIPermissionManager.ALLOW_ACTION);
  695.             }
  696.             catch(e) {
  697.               Cu.reportError(e);
  698.             }
  699.           }
  700.         }
  701.  
  702.         prefBranch.setCharPref(prefName, "");
  703.       }
  704.     }
  705.   },
  706.  
  707.   /**
  708.    * \brief Initialize the Extension Manager Permissions so it includes
  709.    *        the Songbird Add-Ons site as a permitted install location.
  710.    */
  711.   _initRemoteAPIPermissions: function () {
  712.     const httpPrefix = "http://";
  713.     const prefRoot = "rapi.whitelist.add";
  714.  
  715.     var prefBranch =
  716.       Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  717.  
  718.     var ioService =
  719.       Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  720.  
  721.     var permissionManager =
  722.       Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
  723.  
  724.     var childBranches = prefBranch.getChildList(prefRoot, {});
  725.  
  726.     for each (let prefName in childBranches) {
  727.       if(prefBranch.getPrefType(prefName) == Ci.nsIPrefBranch.PREF_STRING) {
  728.         let prefValue = prefBranch.getCharPref(prefName);
  729.         let entries = prefValue.split(",");
  730.         for each (let entry in entries) {
  731.           entry = entry.replace(" ", "", "g");
  732.           if (entry.length > 0) {
  733.             let spec, permissionType;
  734.             [spec, permissionType] = entry.split("=");
  735.             spec = httpPrefix + spec;
  736.             let uri = null;
  737.             if (permissionType) {
  738.               try {
  739.                 uri = ioService.newURI(spec, null, null);
  740.                 permissionManager.add(uri,
  741.                                       "rapi." + permissionType,
  742.                                       Ci.nsIPermissionManager.ALLOW_ACTION);
  743.                 }
  744.               catch(e) {
  745.                 Cu.reportError(e);
  746.               }
  747.             }
  748.           }
  749.         }
  750.  
  751.         prefBranch.setCharPref(prefName, "");
  752.       }
  753.     }
  754.   },
  755.  
  756.   /////////////////////////////////////////////////////////////////////
  757.   // Migrator
  758.   /////////////////////////////////////////////////////////////////////
  759.   /**
  760.    * Responsible for performing version update migrations
  761.    */
  762.   _migrator: {
  763.     /**
  764.      * Handle any necessary migration tasks. Called at application initialization.
  765.      */
  766.     doMigrations: function doMigrations() {
  767.       try {
  768.         this._updateUI();
  769.       } catch (e) {
  770.         Cu.reportError(e);
  771.       }
  772.     },
  773.  
  774.     /**
  775.      * Perform UI migration tasks
  776.      */
  777.     _updateUI: function _updateUI() {
  778.       var prefBranch = Cc["@mozilla.org/preferences-service;1"]
  779.                          .getService(Ci.nsIPrefBranch);
  780.  
  781.       var migration =
  782.         Application.prefs.getValue("songbird.migration.ui.version", 0);
  783.  
  784.       // In 0.5 we added the "media pages" switcher button.
  785.       // Make sure it appears in the nav-bar currentset.
  786.       switch (migration) {
  787.         case 0:
  788.         {
  789.           // Make sure the media page switching is in the web toolbar
  790.           // currentset for each known layout
  791.           var feathersManager = Cc['@songbirdnest.com/songbird/feathersmanager;1']
  792.                                   .getService(Ci.sbIFeathersManager);
  793.           var layouts = feathersManager.getLayoutDescriptions();
  794.           while (layouts.hasMoreElements()) {
  795.             var layoutURL = layouts.getNext()
  796.                                    .QueryInterface(Ci.sbILayoutDescription).url;
  797.             var currentSet =
  798.               LocalStore.getPersistedAttribute(layoutURL,
  799.                                                "nav-bar",
  800.                                                "currentset");
  801.             if (currentSet && currentSet.indexOf("mediapages-container") == -1) {
  802.               currentSet += ",mediapages-container";
  803.               LocalStore.setPersistedAttribute(layoutURL,
  804.                                                "nav-bar",
  805.                                                "currentset",
  806.                                                currentSet);
  807.             }
  808.           }
  809.           LocalStore.flush();
  810.  
  811.           // migrate preferences
  812.           const PREF_OLD_DOWNLOAD_MUSIC_FOLDER =
  813.             "songbird.download.folder";
  814.           const PREF_OLD_DOWNLOAD_MUSIC_ALWAYSPROMPT =
  815.             "songbird.download.always";
  816.           const PREF_DOWNLOAD_MUSIC_FOLDER  =
  817.             "songbird.download.music.folder";
  818.           const PREF_DOWNLOAD_MUSIC_ALWAYSPROMPT =
  819.             "songbird.download.music.alwaysPrompt";
  820.  
  821.           this._migratePref(prefBranch,
  822.                             "setCharPref",
  823.                             "getCharPref",
  824.                             function(p) { return p; },
  825.                             PREF_OLD_DOWNLOAD_MUSIC_FOLDER,
  826.                             PREF_DOWNLOAD_MUSIC_FOLDER);
  827.  
  828.           this._migratePref(prefBranch,
  829.                             "setBoolPref",
  830.                             "getCharPref",
  831.                             function(p) { return p=="1"; },
  832.                             PREF_OLD_DOWNLOAD_MUSIC_ALWAYSPROMPT,
  833.                             PREF_DOWNLOAD_MUSIC_ALWAYSPROMPT);
  834.  
  835.           // update the migration version
  836.           prefBranch.setIntPref("songbird.migration.ui.version", ++migration);
  837.  
  838.           // Fall through to process the next migration.
  839.         }
  840.         case 1:
  841.         {
  842.           // Migrate iTunes importer pref changes introduced in Songbird 1.2
  843.           const PREF_OLD_AUTO_ITUNES_IMPORT =
  844.             "songbird.library_importer.auto_import";
  845.           const PREF_IMPORT_ITUNES =
  846.             "songbird.library_importer.import_tracks";
  847.  
  848.           const PREF_OLD_ITUNES_DONT_IMPORT_PLAYLISTS =
  849.             "songbird.library_importer.dont_import_playlists";
  850.           const PREF_ITUNES_IMPORT_PLAYLISTS =
  851.             "songbird.library_importer.import_playlists";
  852.  
  853.           // Migrating these prefs isn't as easy as |_migratePref|, since the
  854.           // "auto_import" pref will determine the value for "dont import
  855.           // playlists" pref.
  856.           if (Application.prefs.getValue(PREF_OLD_AUTO_ITUNES_IMPORT,
  857.                                               false)) {
  858.             // Migrate the "auto_import" pref.
  859.             Application.prefs.setValue(PREF_IMPORT_ITUNES, true);
  860.  
  861.             // Now migrate the import playlists pref.
  862.             Application.prefs.setValue(
  863.               PREF_ITUNES_IMPORT_PLAYLISTS,
  864.               !Application.prefs.getValue(
  865.                   PREF_OLD_ITUNES_DONT_IMPORT_PLAYLISTS, false)
  866.             );
  867.           }
  868.  
  869.           // update the migration version
  870.           prefBranch.setIntPref("songbird.migration.ui.version", ++migration);
  871.         }
  872.         case 2:
  873.         {
  874.           // Migrate display pane hidden prefs due to display pane changes
  875.           // introduced in Songbird 1.4.1 (Korn)
  876.           const DP =["contentpane_bottom","right_sidebar","servicepane_bottom"];
  877.           for each (var pane in DP) {
  878.             var pref = "songbird.displayPanes.displaypane_" +pane+ ".isHidden";
  879.             Application.prefs.setValue(pref, "0");
  880.           }
  881.  
  882.           // update the migration version
  883.           prefBranch.setIntPref("songbird.migration.ui.version", ++migration);
  884.         }
  885.         case 3:
  886.         {
  887.           // Songbird 1.4.2, bug 19334
  888.           // Force a switch to the default layout & skin
  889.           var defaultLayoutPref = "songbird.feathers.default_main_layout";
  890.           var defaultSkinPref = "songbird.feathers.default_skin_internalname";
  891.           var defaultLayout = Application.prefs.getValue(defaultLayoutPref, "");
  892.           var defaultSkin = Application.prefs.getValue(defaultSkinPref, "");
  893.           var feathersMgr = Cc["@songbirdnest.com/songbird/feathersmanager;1"]
  894.                               .getService(Ci.sbIFeathersManager);
  895.           if (feathersMgr.currentLayoutURL != defaultLayout)
  896.             feathersMgr.switchFeathers(defaultLayout, defaultSkin);
  897.  
  898.           // update the migration version
  899.           prefBranch.setIntPref("songbird.migration.ui.version", ++migration);
  900.         }
  901.         case 4:
  902.         {
  903.           // Songbird 1.10, bug 23299
  904.           // If the user is using media management, turn it off and tell them
  905.           // it's no longer available.
  906.           var mmEnabledPref = "songbird.media_management.library.enabled";
  907.           if (Application.prefs.getValue(mmEnabledPref, false)) {
  908.             // Display the dialog explaining this...
  909.             WindowUtils.openDialog(null,
  910.                                    "chrome://songbird/content/xul/mediaManagementRemovedDialog.xul",
  911.                                    "mediaManagementRemovedDialog",
  912.                                    "chrome,centerscreen",
  913.                                    true);
  914.  
  915.             Application.prefs.setValue(mmEnabledPref, false);
  916.           }
  917.  
  918.           // update migration version
  919.           prefBranch.setIntPref("songbird.migration.ui.version", ++migration);
  920.         }
  921.       }
  922.     },
  923.  
  924.     _migratePref: function(prefBranch, setMethod, getMethod, cvtFunction, oldPrefKey, newPrefKey) {
  925.       // if the old pref exists, do the migration
  926.       if (this._hasPref(prefBranch, getMethod, oldPrefKey)) {
  927.         // but only if the new pref does not exists, otherwise, keep the new pref's value
  928.         if (!this._hasPref(prefBranch, getMethod, newPrefKey)) {
  929.           prefBranch[setMethod](newPrefKey, cvtFunction(prefBranch[getMethod](oldPrefKey)));
  930.         }
  931.         // in every case, get rid of the old pref
  932.         prefBranch.clearUserPref(oldPrefKey);
  933.       }
  934.     },
  935.  
  936.     _hasPref: function(prefBranch, getMethod, prefKey) {
  937.       try {
  938.         var str = prefBranch[getMethod](prefKey);
  939.         return (str && str != "");
  940.       } catch (e) {
  941.         return false;
  942.       }
  943.     },
  944.   },
  945.  
  946.   // XPCOM Goo
  947.   className: SB_APPSTARTUPSERVICE_CLASSNAME,
  948.   classDescription: SB_APPSTARTUPSERVICE_DESC,
  949.   classID: Components.ID(SB_APPSTARTUPSERVICE_CID),
  950.   contractID: SB_APPSTARTUPSERVICE_CONTRACTID,
  951.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
  952. };
  953.  
  954. //------------------------------------------------------------------------------
  955. // XPCOM Registration
  956.  
  957. function NSGetModule(compMgr, fileSpec)
  958. {
  959.   return XPCOMUtils.generateModule([sbAppStartupService],
  960.     function(aCompMgr, aFileSpec, aLocation) {
  961.       XPCOMUtils.categoryManager.addCategoryEntry("app-startup",
  962.                                                   SB_APPSTARTUPSERVICE_DESC,
  963.                                                   "service," +
  964.                                                   SB_APPSTARTUPSERVICE_CONTRACTID,
  965.                                                   true,
  966.                                                   true);
  967.     }
  968.   );
  969. }
  970.  
  971. //------------------------------------------------------------------------------
  972. // Startup command line handler
  973.  
  974. var startupCmdLineHandler = {
  975.   handleFlag: function startupCmdLineHandler_handleFlag(aFlag, aParam) {
  976.     switch (aFlag)
  977.     {
  978.       case "start-in-app-directory" :
  979.         this._handleStartInAppDirectory();
  980.         break;
  981.  
  982.       default :
  983.         return false;
  984.     }
  985.  
  986.     return true;
  987.   },
  988.  
  989.   _handleStartInAppDirectory:
  990.     function startupCmdLineHandler__handleStartInAppDirectory() {
  991.     // Get the application directory.
  992.     var directoryService = Cc["@mozilla.org/file/directory_service;1"]
  993.                              .getService(Ci.nsIProperties);
  994.     var appDirectory = directoryService.get("resource:app", Ci.nsIFile);
  995.  
  996.     // Set the current working directory to the application directory.
  997.     var fileUtils = Cc["@songbirdnest.com/Songbird/FileUtils;1"]
  998.                       .getService(Ci.sbIFileUtils);
  999.     fileUtils.currentDir = appDirectory;
  1000.   },
  1001.  
  1002.   QueryInterface: XPCOMUtils.generateQI([ Ci.sbICommandLineFlagHandler ])
  1003. };
  1004.  
  1005.