home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / audio-video / songbird / Songbird_0.3_windows-i686.exe / components / sbCommandLine.js < prev    next >
Text File  |  2007-10-27  |  13KB  |  407 lines

  1. /**
  2. //
  3. // BEGIN SONGBIRD GPL
  4. //
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2007 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.  
  27. /**
  28.  * \file sbCommandLine.js
  29.  * \brief Implementation of the interface nsICommandLine
  30.  * \todo Implement the -play functionality
  31.  */
  32.  
  33. const Cc = Components.classes;
  34. const Ci = Components.interfaces;
  35. const Cr = Components.results;
  36.  
  37. const SONGBIRD_CLH_CONTRACTID = "@songbirdnest.com/commandlinehandler/general-startup;1?type=songbird";
  38. const SONGBIRD_CLH_CID = Components.ID("{128badd1-aa05-4508-87cc-f3cb3e9b5499}");
  39. const SONGBIRD_CLH_CLASSNAME = "Songbird Command Line Handler";
  40. // "m" for ordinary priority see sbICommandLineHandler.idl
  41. const SONGBIRD_CLH_CATEGORY= "m-songbird-clh";
  42.  
  43. function resolveURIInternal(aCmdLine, aArgument) {
  44.   var uri = aCmdLine.resolveURI(aArgument);
  45.  
  46.   if (!(uri instanceof Components.interfaces.nsIFileURL)) {
  47.     return uri;
  48.   }
  49.  
  50.   return checkUri(uri, aArgument);
  51. }
  52.  
  53. function checkUri(aURI, aURL) {
  54.   try {
  55.     if (aURI instanceof Components.interfaces.nsIFileURL)
  56.       if (aURI.file.exists())
  57.         return aURI;
  58.   }
  59.   catch (e) {
  60.     Components.utils.reportError(e);
  61.   }
  62.  
  63.   // We have interpreted the argument as a relative file URI, but the file
  64.   // doesn't exist. Try URI fixup heuristics: see bug 290782.
  65.  
  66.   try {
  67.     var urifixup = Components.classes["@mozilla.org/docshell/urifixup;1"]
  68.                               .getService(Ci.nsIURIFixup);
  69.  
  70.     aURI = urifixup.createFixupURI(aURL, 0);
  71.   }
  72.   catch (e) {
  73.     Components.utils.reportError(e);
  74.   }
  75.  
  76.   return aURI;
  77. }
  78.  
  79. function shouldLoadURI(aURI) {
  80.   if (!aURI || aURI == "") return false;
  81.   if (aURI && !aURI.schemeIs("chrome"))
  82.     return true;
  83.  
  84.   dump("*** Preventing external load of chrome URI into browser window (" + aURI.spec + ")\n");
  85.   dump("    Use -chrome <uri> instead\n");
  86.   return false;
  87. }
  88.  
  89. /**
  90.  * /brief Songbird commandline handler
  91.  */
  92. function sbCommandLineHandler() {
  93.   this.itemHandlers = []; // array of handlers
  94.   this.itemUriSpecs = []; // array of uri specs
  95.   this.flagHandlers = []; // array of arrays (handler, flag)
  96.   this.flags = [];        // array of arrays (flag, param)
  97. }
  98.  
  99. sbCommandLineHandler.prototype = {
  100.   // there are specific formatting guidelines for help test, see nsICommandLineHandler
  101.   helpInfo : "  -test [tests]        Run tests on the components listed in the\n" +
  102.              "                       optional comma-separated list of tests.\n" +
  103.              "                       If no tests are passed in ALL tests will be run.\n\n" +
  104.              "  [url|path]           Local path/filename to media items to import and play,\n" +
  105.              "                       or URL to load in the browser.\n\n" +
  106.              "  -register-extensions Registers extensions and then quits.\n\n",
  107.   itemHandlers: null,
  108.   itemUriSpecs: null,
  109.   flagHandlers: null,
  110.   flags: null,
  111.  
  112.   handle : function (cmdLine) {
  113.  
  114.     var urilist = [];
  115.     var oldlength = this.itemUriSpecs.length;
  116.  
  117.     if (cmdLine.handleFlag("register-extensions", false)) {
  118.       throw Components.results.NS_ERROR_ABORT;
  119.     }
  120.  
  121.     try {
  122.       var ar;
  123.       while ((ar = cmdLine.handleFlagWithParam("url", false))) {
  124.         urilist.push(resolveURIInternal(cmdLine, ar));
  125.       }
  126.     }
  127.     catch (e) {
  128.       Components.utils.reportError(e);
  129.     }
  130.  
  131.     var tests = null;
  132.     var emptyParam = false;
  133.     try {
  134.       tests = cmdLine.handleFlagWithParam("test", false);
  135.     }
  136.     catch (e) {
  137.       // cmdLine throws if there is no param for the flag, but we want the
  138.       // parameter to be optional, so catch the exception and let ourselves
  139.       // know that things are okay. The flag existed without a param.
  140.       emptyParam = true;
  141.     }
  142.  
  143.     // if there was a parameter or if we had a flag and no param
  144.     if (tests != null || emptyParam) {
  145.       // we're running tests, make sure we don't open a window
  146.       cmdLine.preventDefault = true;
  147.       var testHarness = Cc["@songbirdnest.com/Songbird/TestHarness;1"].getService(Ci.sbITestHarness);
  148.  
  149.       var exception;
  150.       try {
  151.         testHarness.init(tests);
  152.         testHarness.run();
  153.       }
  154.       catch (e) {
  155.         exception = e;
  156.       }
  157.  
  158.       // Fake the sequence of observer notifications for app shutdown. This
  159.       // sequence should match that of canQuitApplication (from
  160.       // globalOverlay.js) and nsAppStartup::Quit (from nsAppStartup.cpp).
  161.       var os = Cc["@mozilla.org/observer-service;1"].
  162.                getService(Ci.nsIObserverService);
  163.  
  164.       // We don't care if anyone tries to cancel quit...
  165.       var dummyCancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
  166.                             createInstance(Ci.nsISupportsPRBool);
  167.       os.notifyObservers(dummyCancelQuit, "quit-application-requested", null);
  168.  
  169.       os.notifyObservers(null, "quit-application-granted", null);
  170.  
  171.       var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
  172.                          .getService(Ci.nsIAppStartup);
  173.       appStartup.quit(Ci.nsIAppStartup.eAttemptQuit);
  174.  
  175.       if (exception) {
  176.         throw Cr.NS_ERROR_ABORT;
  177.       }
  178.     }
  179.  
  180.     // XXX bug 2186
  181.     var count = cmdLine.length;
  182.  
  183.     for (var i = 0; i < count; ++i) {
  184.       var curarg = cmdLine.getArgument(i);
  185.       if (curarg == "") continue;
  186.       if (curarg.match(/^-/)) {
  187.         // Components.utils.reportError("Warning: unrecognized command line flag " + curarg + "\n");
  188.         // To emulate the pre-nsICommandLine behavior, we ignore
  189.         // the argument after an unrecognized flag.
  190.         ++i;
  191.       } else {
  192.         try {
  193.           cmdLine.removeArguments(i, i);
  194.           urilist.push(resolveURIInternal(cmdLine, curarg));
  195.         }
  196.         catch (e) {
  197.           Components.utils.reportError("Error opening URI '" + curarg + "' from the command line: " + e + "\n");
  198.         }
  199.       }
  200.     }
  201.  
  202.     for (var uri in urilist) {
  203.       if (shouldLoadURI(urilist[uri])) {
  204.         this.itemUriSpecs.push(urilist[uri].spec);
  205.       }
  206.     }
  207.  
  208.     if (this.itemUriSpecs.length > oldlength)
  209.       this.dispatchItems();
  210.  
  211.     this.handleRemainingFlags(cmdLine);
  212.     this.dispatchFlags();
  213.   },
  214.  
  215.   handleURL: function(aURL) {
  216.     var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  217.                               .getService(Components.interfaces.nsIIOService);
  218.     var uri = ioService.newURI(aURL, null, null);
  219.     uri = checkUri(uri, aURL);
  220.     if (shouldLoadURI(uri)) {
  221.       this.itemUriSpecs.push(uri.spec);
  222.       this.dispatchItems();
  223.     }
  224.   },
  225.  
  226.   handleRemainingFlags: function(cmdLine) {
  227.     while (cmdLine.length) {
  228.       var curarg = cmdLine.getArgument(0);
  229.       if (curarg.match(/^-/)) {
  230.         var flag = curarg.slice(1);
  231.  
  232.         var param;
  233.  
  234.         var emptyParam = false;
  235.         try {
  236.           param = cmdLine.handleFlagWithParam(flag, false);
  237.         }
  238.         catch (e) {
  239.           // cmdLine throws if there is no param for the flag, but we want the
  240.           // parameter to be optional, so catch the exception and let ourselves
  241.           // know that things are okay. The flag existed without a param.
  242.           emptyParam = true;
  243.           cmdLine.handleFlag(flag, false);
  244.         }
  245.  
  246.         // if there was a parameter or if we had a flag and no param
  247.         if (param != null || emptyParam) {
  248.           // record the flag for handling by flag handlers
  249.           this.flags.push([flag, param]);
  250.         }
  251.       } else {
  252.         // this should really not occur, because the case should have
  253.         // been handled as a play item earlier. however, if for some reason
  254.         // it does occur, not doing the following would cause an infinite loop,
  255.         // so do it just in case.
  256.         cmdLine.removeArguments(0, 0);
  257.       }
  258.     }
  259.   },
  260.  
  261.   addItemHandler: function (aHandler) {
  262.     this.itemHandlers.push(aHandler);
  263.     // dispatch unhandled items immediatly to this handler
  264.     this.dispatchItemsToHandler(aHandler);
  265.   },
  266.  
  267.   removeItemHandler: function(aHandler) {
  268.     var index = this.itemHandlers.indexOf(aHandler);
  269.     if (index != -1) this.itemHandlers.splice(index, 1);
  270.   },
  271.  
  272.   dispatchItemsToHandler: function(aHandler) {
  273.     var count = 0;
  274.     var total = this.itemUriSpecs.length;
  275.     for (var i=0; i < this.itemUriSpecs.length; i++) {
  276.       if (aHandler.handleItem(this.itemUriSpecs[i], count++, total)) {
  277.         this.itemUriSpecs.splice(i--, 1);
  278.       }
  279.     }
  280.   },
  281.  
  282.   dispatchItems: function() {
  283.     // The last handler to get registered gets
  284.     // priority over the first ones, so that if
  285.     // there are several instances of the main window,
  286.     // the items open in the one created last.
  287.     for (var handleridx = this.itemHandlers.length-1; handleridx >= 0; handleridx--) {
  288.       this.dispatchItemsToHandler(this.itemHandlers[handleridx]);
  289.       if (this.itemUriSpecs.length == 0) break;
  290.     }
  291.   },
  292.  
  293.   addFlagHandler: function (aHandler, aFlag) {
  294.     var entry = [aHandler, aFlag];
  295.     this.flagHandlers.push(entry);
  296.     // dispatch unhandled flags immediatly to this handler
  297.     this.dispatchFlagsToHandler(entry);
  298.   },
  299.  
  300.   removeFlagHandler: function(aHandler, aFlag) {
  301.     for (var i=this.flagHandlers.length-1;i>=0;i--) {
  302.       var entry = this.flagHandlers[i];
  303.       if (entry[0] == aHandler && entry[1] == aFlag) {
  304.         this.flagHandlers.splice(i, 1);
  305.         return;
  306.       }
  307.     }
  308.   },
  309.  
  310.   dispatchFlagsToHandler: function(aHandlerEntry) {
  311.     var handler = aHandlerEntry[0];
  312.     var flag = aHandlerEntry[1];
  313.     for (var i=0; i < this.flags.length; i++) {
  314.       if (this.flags[i][0] == flag) {
  315.         if (handler.handleFlag(this.flags[i][0], this.flags[i][1])) {
  316.           this.flags.splice(i--, 1);
  317.         }
  318.       }
  319.     }
  320.   },
  321.  
  322.   dispatchFlags: function() {
  323.     // The last handler to get registered gets
  324.     // priority over the first ones, so that if
  325.     // there are several instances of the main window,
  326.     // the flags are handled by the one created last.
  327.     for (var handleridx = this.flagHandlers.length-1; handleridx >= 0; handleridx--) {
  328.       this.dispatchFlagsToHandler(this.flagHandlers[handleridx]);
  329.       if (this.flags.length == 0) break;
  330.     }
  331.   },
  332.  
  333.   QueryInterface : function clh_QI(iid) {
  334.     if (iid.equals(Ci.nsICommandLineHandler) ||
  335.         iid.equals(Ci.sbICommandLineManager) ||
  336.         iid.equals(Ci.nsISupports))
  337.       return this;
  338.  
  339.     throw Cr.NS_ERROR_NO_INTERFACE;
  340.   }
  341. }; // sbCommandeLineHandler
  342.  
  343. /**
  344.  * /brief The module for getting the commandline handler
  345.  */
  346. const sbCommandLineHandlerModule = {
  347.   registerSelf : function (compMgr, fileSpec, location, type) {
  348.     compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  349.     compMgr.registerFactoryLocation(SONGBIRD_CLH_CID,
  350.                                     SONGBIRD_CLH_CLASSNAME,
  351.                                     SONGBIRD_CLH_CONTRACTID,
  352.                                     fileSpec,
  353.                                     location,
  354.                                     type);
  355.  
  356.     var catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  357.     catMan.addCategoryEntry("command-line-handler",
  358.                             SONGBIRD_CLH_CATEGORY,
  359.                             SONGBIRD_CLH_CONTRACTID,
  360.                             true,
  361.                             true);
  362.   },
  363.  
  364.   getClassObject : function (compMgr, cid, iid) {
  365.     if (!cid.equals(SONGBIRD_CLH_CID))
  366.       throw Cr.NS_ERROR_NO_INTERFACE;
  367.  
  368.     if (!iid.equals(Ci.nsIFactory))
  369.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  370.  
  371.     return this.mFactory;
  372.   },
  373.  
  374.   mFactory : {
  375.     createInstance : function (outer, iid) {
  376.       if (outer != null)
  377.         throw Cr.NS_ERROR_NO_AGGREGATION;
  378.       return (new sbCommandLineHandler()).QueryInterface(iid);
  379.     }
  380.   },
  381.  
  382.   unregisterSelf : function (compMgr, location, type) {
  383.     compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  384.     compMgr.unregisterFactoryLocation(SONGBIRD_CLH_CID, location);
  385.  
  386.     var catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  387.     catMan.deleteCategoryEntry("command-line-handler", SONGBIRD_CLH_CATEGORY);
  388.   },
  389.  
  390.   canUnload : function (compMgr) {
  391.     return true;
  392.   },
  393.  
  394.   QueryInterface : function (iid) {
  395.     if ( !iid.equals(Ci.nsIModule) ||
  396.          !iid.equals(Ci.nsISupports) )
  397.       throw Cr.NS_ERROR_NO_INTERFACE;
  398.     return this;
  399.   }
  400.  
  401. }; // sbCommandLineHandlerModule
  402.  
  403. function NSGetModule(comMgr, fileSpec)
  404. {
  405.   return sbCommandLineHandlerModule;
  406. }
  407.