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 / xulrunner / components / nsHandlerService.js < prev    next >
Text File  |  2007-10-19  |  48KB  |  1,292 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is the Mozilla browser.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Myk Melez <myk@mozilla.org>
  22.  *   Dan Mosedale <dmose@mozilla.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const Ci = Components.interfaces;
  39. const Cc = Components.classes;
  40. const Cu = Components.utils;
  41. const Cr = Components.results;
  42.  
  43.  
  44. const CLASS_MIMEINFO        = "mimetype";
  45. const CLASS_PROTOCOLINFO    = "scheme";
  46.  
  47.  
  48. // namespace prefix
  49. const NC_NS                 = "http://home.netscape.com/NC-rdf#";
  50.  
  51. // the most recent default handlers that have been injected
  52. const NC_DEFAULT_HANDLERS_VERSION = NC_NS + "defaultHandlersVersion";
  53.  
  54. // type list properties
  55.  
  56. const NC_MIME_TYPES         = NC_NS + "MIME-types";
  57. const NC_PROTOCOL_SCHEMES   = NC_NS + "Protocol-Schemes";
  58.  
  59. // content type ("type") properties
  60.  
  61. // nsIHandlerInfo::type
  62. const NC_VALUE              = NC_NS + "value";
  63. const NC_DESCRIPTION        = NC_NS + "description";
  64.  
  65. // additional extensions
  66. const NC_FILE_EXTENSIONS    = NC_NS + "fileExtensions";
  67.  
  68. // references nsIHandlerInfo record
  69. const NC_HANDLER_INFO       = NC_NS + "handlerProp";
  70.  
  71. // handler info ("info") properties
  72.  
  73. // nsIHandlerInfo::preferredAction
  74. const NC_SAVE_TO_DISK       = NC_NS + "saveToDisk";
  75. const NC_HANDLE_INTERNALLY  = NC_NS + "handleInternal";
  76. const NC_USE_SYSTEM_DEFAULT = NC_NS + "useSystemDefault";
  77.  
  78. // nsIHandlerInfo::alwaysAskBeforeHandling
  79. const NC_ALWAYS_ASK         = NC_NS + "alwaysAsk";
  80.  
  81. // references nsIHandlerApp records
  82. const NC_PREFERRED_APP      = NC_NS + "externalApplication";
  83. const NC_POSSIBLE_APP       = NC_NS + "possibleApplication";
  84.  
  85. // handler app ("handler") properties
  86.  
  87. // nsIHandlerApp::name
  88. const NC_PRETTY_NAME        = NC_NS + "prettyName";
  89.  
  90. // nsILocalHandlerApp::executable
  91. const NC_PATH               = NC_NS + "path";
  92.  
  93. // nsIWebHandlerApp::uriTemplate
  94. const NC_URI_TEMPLATE       = NC_NS + "uriTemplate";
  95.  
  96.  
  97. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  98.  
  99.  
  100. function HandlerService() {
  101.   this._init();
  102. }
  103.  
  104. HandlerService.prototype = {
  105.   //**************************************************************************//
  106.   // XPCOM Plumbing
  107.  
  108.   classDescription: "Handler Service",
  109.   classID:          Components.ID("{32314cc8-22f7-4f7f-a645-1a45453ba6a6}"),
  110.   contractID:       "@mozilla.org/uriloader/handler-service;1",
  111.   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIHandlerService]),
  112.  
  113.  
  114.   //**************************************************************************//
  115.   // Initialization & Destruction
  116.   
  117.   _init: function HS__init() {
  118.     // Observe profile-before-change so we can switch to the datasource
  119.     // in the new profile when the user changes profiles.
  120.     this._observerSvc.addObserver(this, "profile-before-change", false);
  121.  
  122.     // Observe xpcom-shutdown so we can remove these observers
  123.     // when the application shuts down.
  124.     this._observerSvc.addObserver(this, "xpcom-shutdown", false);
  125.  
  126.     // Observe profile-do-change so that non-default profiles get upgraded too
  127.     this._observerSvc.addObserver(this, "profile-do-change", false);
  128.     
  129.     // do any necessary updating of the datastore
  130.     this._updateDB();
  131.   },
  132.  
  133.   _updateDB: function HS__updateDB() {
  134.     // if the default prefs have changed, inject any new default handers
  135.     // into the datastore 
  136.     try { 
  137.       if (this._datastoreDefaultHandlersVersion <
  138.           this._prefsDefaultHandlersVersion) {
  139.         this._injectNewDefaults();
  140.         this._datastoreDefaultHandlersVersion = 
  141.         this._prefsDefaultHandlersVersion;
  142.       }
  143.     } catch (ex) {
  144.       // if injecting the defaults failed, life goes on...
  145.     }
  146.   },
  147.   
  148.   _destroy: function HS__destroy() {
  149.     this._observerSvc.removeObserver(this, "profile-before-change");
  150.     this._observerSvc.removeObserver(this, "xpcom-shutdown");
  151.     this._observerSvc.removeObserver(this, "profile-do-change");
  152.  
  153.     // XXX Should we also null references to all the services that get stored
  154.     // by our memoizing getters in the Convenience Getters section?
  155.   },
  156.  
  157.   _onProfileChange: function HS__onProfileChange() {
  158.     // Lose our reference to the datasource so we reacquire it
  159.     // from the new profile the next time we need it.
  160.     this.__ds = null;
  161.   },
  162.  
  163.   _isInHandlerArray: function HS__isInHandlerArray(aArray, aHandler) {
  164.     var enumerator = aArray.enumerate();
  165.     while (enumerator.hasMoreElements()) {
  166.       let handler = enumerator.getNext();
  167.       handler.QueryInterface(Ci.nsIHandlerApp);
  168.       if (handler.equals(aHandler))
  169.         return true;
  170.     }
  171.     
  172.     return false;
  173.   },
  174.  
  175.   get _datastoreDefaultHandlersVersion() {
  176.     var version = this._getValue("urn:root", NC_DEFAULT_HANDLERS_VERSION); 
  177.     
  178.     version = version ? version : -1;
  179.     
  180.     return version;
  181.   },
  182.  
  183.   set _datastoreDefaultHandlersVersion(aNewVersion) {
  184.     return this._setLiteral("urn:root", NC_DEFAULT_HANDLERS_VERSION, 
  185.                             aNewVersion);
  186.   },
  187.  
  188.   get _prefsDefaultHandlersVersion() {
  189.     // get handler service pref branch
  190.     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  191.                   getService(Ci.nsIPrefService);
  192.     var handlerSvcBranch = prefSvc.getBranch("gecko.handlerService.");
  193.   
  194.     // get the version of the preferences for this locale
  195.     var version = handlerSvcBranch.getComplexValue("defaultHandlersVersion",
  196.                                                    Ci.nsISupportsString).data;
  197.                                                    
  198.     return version;                                                   
  199.   },
  200.   
  201.   _injectNewDefaults: function HS__injectNewDefaults() {
  202.     // get handler service pref branch
  203.     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  204.                   getService(Ci.nsIPrefService);
  205.  
  206.     let schemesPrefBranch = prefSvc.getBranch("gecko.handlerService.schemes.");
  207.     let schemePrefList = schemesPrefBranch.getChildList("", {}); 
  208.  
  209.     let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
  210.                    getService(Ci.nsIExternalProtocolService);
  211.  
  212.     var schemes = {};
  213.  
  214.     // read all the scheme prefs into a hash
  215.     for each (var schemePrefName in schemePrefList) {
  216.  
  217.       let [scheme, handlerNumber, attribute] = schemePrefName.split(".");
  218.  
  219.       if (!(scheme in schemes))
  220.         schemes[scheme] = {};
  221.       if (!(handlerNumber in schemes[scheme]))
  222.         schemes[scheme][handlerNumber] = {};
  223.         
  224.       schemes[scheme][handlerNumber][attribute] = 
  225.         schemesPrefBranch.getComplexValue(schemePrefName,
  226.                                           Ci.nsISupportsString).data;
  227.     }
  228.  
  229.     for (var scheme in schemes) {
  230.  
  231.       // get a protocol info object for that scheme and cache the possible
  232.       // handlers to avoid extra xpconnect traversals
  233.       let protoInfo = protoSvc.getProtocolHandlerInfo(scheme);  
  234.       let possibleHandlers = protoInfo.possibleApplicationHandlers;
  235.  
  236.       for each (var handlerPrefs in schemes[scheme]) {
  237.  
  238.         let handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
  239.                          createInstance(Ci.nsIWebHandlerApp);
  240.  
  241.         handlerApp.uriTemplate = handlerPrefs.uriTemplate;
  242.         handlerApp.name = handlerPrefs.name;                
  243.  
  244.         if (!this._isInHandlerArray(possibleHandlers, handlerApp)) {
  245.              possibleHandlers.appendElement(handlerApp, false);
  246.         }
  247.       }
  248.  
  249.       this.store(protoInfo);
  250.     }
  251.   },
  252.  
  253.   //**************************************************************************//
  254.   // nsIObserver
  255.   
  256.   observe: function HS__observe(subject, topic, data) {
  257.     switch(topic) {
  258.       case "profile-before-change":
  259.         this._onProfileChange();
  260.         break;
  261.       case "xpcom-shutdown":
  262.         this._destroy();
  263.         break;
  264.       case "profile-do-change":
  265.         this._updateDB();
  266.         break;  
  267.     }
  268.   },
  269.  
  270.  
  271.   //**************************************************************************//
  272.   // nsIHandlerService
  273.  
  274.   enumerate: function HS_enumerate() {
  275.     var handlers = Cc["@mozilla.org/array;1"].
  276.                    createInstance(Ci.nsIMutableArray);
  277.     this._appendHandlers(handlers, CLASS_MIMEINFO);
  278.     this._appendHandlers(handlers, CLASS_PROTOCOLINFO);
  279.     return handlers.enumerate();
  280.   },
  281.  
  282.   fillHandlerInfo: function HS_fillHandlerInfo(aHandlerInfo, aOverrideType) {
  283.     var type = aOverrideType || aHandlerInfo.type;
  284.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), type);
  285.  
  286.     // Determine whether or not information about this handler is available
  287.     // in the datastore by looking for its "value" property, which stores its
  288.     // type and should always be present.
  289.     if (!this._hasValue(typeID, NC_VALUE))
  290.       throw Cr.NS_ERROR_NOT_AVAILABLE;
  291.  
  292.     // Retrieve the human-readable description of the type.
  293.     if (this._hasValue(typeID, NC_DESCRIPTION))
  294.       aHandlerInfo.description = this._getValue(typeID, NC_DESCRIPTION);
  295.  
  296.     // Note: for historical reasons, we don't actually check that the type
  297.     // record has a "handlerProp" property referencing the info record.  It's
  298.     // unclear whether or not we should start doing this check; perhaps some
  299.     // legacy datasources don't have such references.
  300.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), type);
  301.  
  302.     aHandlerInfo.preferredAction = this._retrievePreferredAction(infoID);
  303.  
  304.     var preferredHandlerID =
  305.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), type);
  306.  
  307.     // Retrieve the preferred handler.
  308.     // Note: for historical reasons, we don't actually check that the info
  309.     // record has an "externalApplication" property referencing the preferred
  310.     // handler record.  It's unclear whether or not we should start doing
  311.     // this check; perhaps some legacy datasources don't have such references.
  312.     aHandlerInfo.preferredApplicationHandler =
  313.       this._retrieveHandlerApp(preferredHandlerID);
  314.  
  315.     // Fill the array of possible handlers with the ones in the datastore.
  316.     this._fillPossibleHandlers(infoID,
  317.                                aHandlerInfo.possibleApplicationHandlers,
  318.                                aHandlerInfo.preferredApplicationHandler);
  319.  
  320.     // Retrieve the "always ask" flag.
  321.     // Note: we only set the flag to false if we are absolutely sure the user
  322.     // does not want to be asked.  Any sort of bogus data should mean we ask.
  323.     // So there must be an "alwaysAsk" property in the datastore for the handler
  324.     // info object, and it must be set to "false", in order for us not to ask.
  325.     aHandlerInfo.alwaysAskBeforeHandling =
  326.       !this._hasValue(infoID, NC_ALWAYS_ASK) ||
  327.       this._getValue(infoID, NC_ALWAYS_ASK) != "false";
  328.  
  329.     // If the object represents a MIME type handler, then also retrieve
  330.     // any file extensions.
  331.     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
  332.       for each (let fileExtension in this._retrieveFileExtensions(typeID))
  333.         aHandlerInfo.appendExtension(fileExtension);
  334.   },
  335.  
  336.   store: function HS_store(aHandlerInfo) {
  337.     // FIXME: when we switch from RDF to something with transactions (like
  338.     // SQLite), enclose the following changes in a transaction so they all
  339.     // get rolled back if any of them fail and we don't leave the datastore
  340.     // in an inconsistent state.
  341.  
  342.     this._ensureRecordsForType(aHandlerInfo);
  343.     this._storePreferredAction(aHandlerInfo);
  344.     this._storePreferredHandler(aHandlerInfo);
  345.     this._storePossibleHandlers(aHandlerInfo);
  346.     this._storeAlwaysAsk(aHandlerInfo);
  347.  
  348.     // Write the changes to the database immediately so we don't lose them
  349.     // if the application crashes.
  350.     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
  351.       this._ds.Flush();
  352.   },
  353.  
  354.   exists: function HS_exists(aHandlerInfo) {
  355.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  356.     return this._hasLiteralAssertion(typeID, NC_VALUE, aHandlerInfo.type);
  357.   },
  358.  
  359.   remove: function HS_remove(aHandlerInfo) {
  360.     var preferredHandlerID =
  361.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  362.     this._removeAssertions(preferredHandlerID);
  363.  
  364.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  365.  
  366.     // Get a list of possible handlers.  After we have removed the info record,
  367.     // we'll check if any other info records reference these handlers, and we'll
  368.     // remove the handler records that aren't referenced by other info records.
  369.     var possibleHandlerIDs = [];
  370.     var possibleHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
  371.     while (possibleHandlerTargets.hasMoreElements()) {
  372.       let possibleHandlerTarget = possibleHandlerTargets.getNext();
  373.       // Note: possibleHandlerTarget should always be an nsIRDFResource.
  374.       // The conditional is just here in case of a corrupt RDF datasource.
  375.       if (possibleHandlerTarget instanceof Ci.nsIRDFResource)
  376.         possibleHandlerIDs.push(possibleHandlerTarget.ValueUTF8);
  377.     }
  378.  
  379.     // Remove the info record.
  380.     this._removeAssertions(infoID);
  381.  
  382.     // Now that we've removed the info record, remove any possible handlers
  383.     // that aren't referenced by other info records.
  384.     for each (let possibleHandlerID in possibleHandlerIDs)
  385.       if (!this._existsResourceTarget(NC_POSSIBLE_APP, possibleHandlerID))
  386.         this._removeAssertions(possibleHandlerID);
  387.  
  388.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  389.     this._removeAssertions(typeID);
  390.  
  391.     // Now that there's no longer a handler for this type, remove the type
  392.     // from the list of types for which there are known handlers.
  393.     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
  394.     var type = this._rdf.GetResource(typeID);
  395.     var typeIndex = typeList.IndexOf(type);
  396.     if (typeIndex != -1)
  397.       typeList.RemoveElementAt(typeIndex, true);
  398.  
  399.     // Write the changes to the database immediately so we don't lose them
  400.     // if the application crashes.
  401.     // XXX If we're removing a bunch of handlers at once, will flushing
  402.     // after every removal cause a significant performance hit?
  403.     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
  404.       this._ds.Flush();
  405.   },
  406.  
  407.   getTypeFromExtension: function HS_getTypeFromExtension(aFileExtension) {
  408.     var fileExtension = aFileExtension.toLowerCase();
  409.     var typeID;
  410.  
  411.     if (this._existsLiteralTarget(NC_FILE_EXTENSIONS, fileExtension))
  412.       typeID = this._getSourceForLiteral(NC_FILE_EXTENSIONS, fileExtension);
  413.  
  414.     if (typeID && this._hasValue(typeID, NC_VALUE)) {
  415.       let type = this._getValue(typeID, NC_VALUE);
  416.       if (type == "")
  417.         throw Cr.NS_ERROR_FAILURE;
  418.       return type;
  419.     }
  420.  
  421.     throw Cr.NS_ERROR_NOT_AVAILABLE;
  422.   },
  423.  
  424.  
  425.   //**************************************************************************//
  426.   // Retrieval Methods
  427.  
  428.   /**
  429.    * Retrieve the preferred action for the info record with the given ID.
  430.    *
  431.    * @param aInfoID  {string}  the info record ID
  432.    *
  433.    * @returns  {integer}  the preferred action enumeration value
  434.    */
  435.   _retrievePreferredAction: function HS__retrievePreferredAction(aInfoID) {
  436.     if (this._getValue(aInfoID, NC_SAVE_TO_DISK) == "true")
  437.       return Ci.nsIHandlerInfo.saveToDisk;
  438.     
  439.     if (this._getValue(aInfoID, NC_USE_SYSTEM_DEFAULT) == "true")
  440.       return Ci.nsIHandlerInfo.useSystemDefault;
  441.     
  442.     if (this._getValue(aInfoID, NC_HANDLE_INTERNALLY) == "true")
  443.       return Ci.nsIHandlerInfo.handleInternal;
  444.  
  445.     return Ci.nsIHandlerInfo.useHelperApp;
  446.   },
  447.  
  448.   /**
  449.    * Fill an array of possible handlers with the handlers for the given info ID.
  450.    *
  451.    * @param aInfoID            {string}           the ID of the info record
  452.    * @param aPossibleHandlers  {nsIMutableArray}  the array of possible handlers
  453.    * @param aPreferredHandler  {nsIHandlerApp}    the preferred handler, if any
  454.    */
  455.   _fillPossibleHandlers: function HS__fillPossibleHandlers(aInfoID,
  456.                                                            aPossibleHandlers,
  457.                                                            aPreferredHandler) {
  458.     // The set of possible handlers should include the preferred handler,
  459.     // but legacy datastores (from before we added possible handlers) won't
  460.     // include the preferred handler, so check if it's included as we build
  461.     // the list of handlers, and, if it's not included, add it to the list.
  462.     if (aPreferredHandler)
  463.       aPossibleHandlers.appendElement(aPreferredHandler, false);
  464.  
  465.     var possibleHandlerTargets = this._getTargets(aInfoID, NC_POSSIBLE_APP);
  466.  
  467.     while (possibleHandlerTargets.hasMoreElements()) {
  468.       let possibleHandlerTarget = possibleHandlerTargets.getNext();
  469.       if (!(possibleHandlerTarget instanceof Ci.nsIRDFResource))
  470.         continue;
  471.  
  472.       let possibleHandlerID = possibleHandlerTarget.ValueUTF8;
  473.       let possibleHandler = this._retrieveHandlerApp(possibleHandlerID);
  474.       if (possibleHandler && (!aPreferredHandler ||
  475.                               !possibleHandler.equals(aPreferredHandler)))
  476.         aPossibleHandlers.appendElement(possibleHandler, false);
  477.     }
  478.   },
  479.  
  480.   /**
  481.    * Retrieve the handler app object with the given ID.
  482.    *
  483.    * @param aHandlerAppID  {string}  the ID of the handler app to retrieve
  484.    *
  485.    * @returns  {nsIHandlerApp}  the handler app, if any; otherwise null
  486.    */
  487.   _retrieveHandlerApp: function HS__retrieveHandlerApp(aHandlerAppID) {
  488.     var handlerApp;
  489.  
  490.     // If it has a path, it's a local handler; otherwise, it's a web handler.
  491.     if (this._hasValue(aHandlerAppID, NC_PATH)) {
  492.       let executable =
  493.         this._getFileWithPath(this._getValue(aHandlerAppID, NC_PATH));
  494.       if (!executable)
  495.         return null;
  496.  
  497.       handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
  498.                    createInstance(Ci.nsILocalHandlerApp);
  499.       handlerApp.executable = executable;
  500.     }
  501.     else if (this._hasValue(aHandlerAppID, NC_URI_TEMPLATE)) {
  502.       let uriTemplate = this._getValue(aHandlerAppID, NC_URI_TEMPLATE);
  503.       if (!uriTemplate)
  504.         return null;
  505.  
  506.       handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
  507.                    createInstance(Ci.nsIWebHandlerApp);
  508.       handlerApp.uriTemplate = uriTemplate;
  509.     }
  510.     else
  511.       return null;
  512.  
  513.     handlerApp.name = this._getValue(aHandlerAppID, NC_PRETTY_NAME);
  514.  
  515.     return handlerApp;
  516.   },
  517.  
  518.   /*
  519.    * Retrieve file extensions, if any, for the MIME type with the given type ID.
  520.    *
  521.    * @param aTypeID  {string}  the type record ID
  522.    */
  523.   _retrieveFileExtensions: function HS__retrieveFileExtensions(aTypeID) {
  524.     var fileExtensions = [];
  525.  
  526.     var fileExtensionTargets = this._getTargets(aTypeID, NC_FILE_EXTENSIONS);
  527.  
  528.     while (fileExtensionTargets.hasMoreElements()) {
  529.       let fileExtensionTarget = fileExtensionTargets.getNext();
  530.       if (fileExtensionTarget instanceof Ci.nsIRDFLiteral &&
  531.           fileExtensionTarget.Value != "")
  532.         fileExtensions.push(fileExtensionTarget.Value);
  533.     }
  534.  
  535.     return fileExtensions;
  536.   },
  537.  
  538.   /**
  539.    * Get the file with the given path.  This is not as simple as merely
  540.    * initializing a local file object with the path, because the path might be
  541.    * relative to the current process directory, in which case we have to
  542.    * construct a path starting from that directory.
  543.    *
  544.    * @param aPath  {string}  a path to a file
  545.    *
  546.    * @returns {nsILocalFile} the file, or null if the file does not exist
  547.    */
  548.   _getFileWithPath: function HS__getFileWithPath(aPath) {
  549.     var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  550.  
  551.     try {
  552.       file.initWithPath(aPath);
  553.  
  554.       if (file.exists())
  555.         return file;
  556.     }
  557.     catch(ex) {
  558.       // Note: for historical reasons, we don't actually check to see
  559.       // if the exception is NS_ERROR_FILE_UNRECOGNIZED_PATH, which is what
  560.       // nsILocalFile::initWithPath throws when a path is relative.
  561.  
  562.       file = this._dirSvc.get("XCurProcD", Ci.nsIFile);
  563.  
  564.       try {
  565.         file.append(aPath);
  566.         if (file.exists())
  567.           return file;
  568.       }
  569.       catch(ex) {}
  570.     }
  571.  
  572.     return null;
  573.   },
  574.  
  575.  
  576.   //**************************************************************************//
  577.   // Storage Methods
  578.  
  579.   _storePreferredAction: function HS__storePreferredAction(aHandlerInfo) {
  580.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  581.  
  582.     switch(aHandlerInfo.preferredAction) {
  583.       case Ci.nsIHandlerInfo.saveToDisk:
  584.         this._setLiteral(infoID, NC_SAVE_TO_DISK, "true");
  585.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  586.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  587.         break;
  588.  
  589.       case Ci.nsIHandlerInfo.handleInternally:
  590.         this._setLiteral(infoID, NC_HANDLE_INTERNALLY, "true");
  591.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  592.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  593.         break;
  594.  
  595.       case Ci.nsIHandlerInfo.useSystemDefault:
  596.         this._setLiteral(infoID, NC_USE_SYSTEM_DEFAULT, "true");
  597.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  598.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  599.         break;
  600.  
  601.       // This value is indicated in the datastore either by the absence of
  602.       // the three properties or by setting them all "false".  Of these two
  603.       // options, the former seems preferable, because it reduces the size
  604.       // of the RDF file and thus the amount of stuff we have to parse.
  605.       case Ci.nsIHandlerInfo.useHelperApp:
  606.       default:
  607.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  608.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  609.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  610.         break;
  611.     }
  612.   },
  613.  
  614.   _storePreferredHandler: function HS__storePreferredHandler(aHandlerInfo) {
  615.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  616.     var handlerID =
  617.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  618.  
  619.     var handler = aHandlerInfo.preferredApplicationHandler;
  620.  
  621.     if (handler) {
  622.       this._storeHandlerApp(handlerID, handler);
  623.  
  624.       // Make this app be the preferred app for the handler info.
  625.       //
  626.       // Note: nsExternalHelperAppService::FillContentHandlerProperties ignores
  627.       // this setting and instead identifies the preferred app as the resource
  628.       // whose URI follows the pattern urn:<class>:externalApplication:<type>.
  629.       // But the old downloadactions.js code used to set this property, so just
  630.       // in case there is still some code somewhere that relies on its presence,
  631.       // we set it here.
  632.       this._setResource(infoID, NC_PREFERRED_APP, handlerID);
  633.     }
  634.     else {
  635.       // There isn't a preferred handler.  Remove the existing record for it,
  636.       // if any.
  637.       this._removeTarget(infoID, NC_PREFERRED_APP);
  638.       this._removeAssertions(handlerID);
  639.     }
  640.   },
  641.  
  642.   /**
  643.    * Store the list of possible handler apps for the content type represented
  644.    * by the given handler info object.
  645.    *
  646.    * @param aHandlerInfo  {nsIHandlerInfo}  the handler info object
  647.    */
  648.   _storePossibleHandlers: function HS__storePossibleHandlers(aHandlerInfo) {
  649.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  650.  
  651.     // First, retrieve the set of handler apps currently stored for the type,
  652.     // keeping track of their IDs in a hash that we'll use to determine which
  653.     // ones are no longer valid and should be removed.
  654.     var currentHandlerApps = {};
  655.     var currentHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
  656.     while (currentHandlerTargets.hasMoreElements()) {
  657.       let handlerApp = currentHandlerTargets.getNext();
  658.       if (handlerApp instanceof Ci.nsIRDFResource) {
  659.         let handlerAppID = handlerApp.ValueUTF8;
  660.         currentHandlerApps[handlerAppID] = true;
  661.       }
  662.     }
  663.  
  664.     // Next, store any new handler apps.
  665.     var newHandlerApps =
  666.       aHandlerInfo.possibleApplicationHandlers.enumerate();
  667.     while (newHandlerApps.hasMoreElements()) {
  668.       let handlerApp =
  669.         newHandlerApps.getNext().QueryInterface(Ci.nsIHandlerApp);
  670.       let handlerAppID = this._getPossibleHandlerAppID(handlerApp);
  671.       if (!this._hasResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID)) {
  672.         this._storeHandlerApp(handlerAppID, handlerApp);
  673.         this._addResourceTarget(infoID, NC_POSSIBLE_APP, handlerAppID);
  674.       }
  675.       delete currentHandlerApps[handlerAppID];
  676.     }
  677.  
  678.     // Finally, remove any old handler apps that aren't being used anymore,
  679.     // and if those handler apps aren't being used by any other type either,
  680.     // then completely remove their record from the datastore so we don't
  681.     // leave it clogged up with information about handler apps we don't care
  682.     // about anymore.
  683.     for (let handlerAppID in currentHandlerApps) {
  684.       this._removeTarget(infoID, NC_POSSIBLE_APP, handlerAppID);
  685.       if (!this._existsResourceTarget(NC_POSSIBLE_APP, handlerAppID))
  686.         this._removeAssertions(handlerAppID);
  687.     }
  688.   },
  689.  
  690.   /**
  691.    * Store the given handler app.
  692.    *
  693.    * Note: the reason this method takes the ID of the handler app in a param
  694.    * is that the ID is different than it usually is when the handler app
  695.    * in question is a preferred handler app, so this method can't just derive
  696.    * the ID of the handler app by calling _getPossibleHandlerAppID, its callers
  697.    * have to do that for it.
  698.    *
  699.    * @param aHandlerAppID {string}        the ID of the handler app to store
  700.    * @param aHandlerApp   {nsIHandlerApp} the handler app to store
  701.    */
  702.   _storeHandlerApp: function HS__storeHandlerApp(aHandlerAppID, aHandlerApp) {
  703.     aHandlerApp.QueryInterface(Ci.nsIHandlerApp);
  704.     this._setLiteral(aHandlerAppID, NC_PRETTY_NAME, aHandlerApp.name);
  705.  
  706.     // In the case of the preferred handler, the handler ID could have been
  707.     // used to refer to a different kind of handler in the past (i.e. either
  708.     // a local hander or a web handler), so if the new handler is a local
  709.     // handler, then we remove any web handler properties and vice versa.
  710.     // This is unnecessary but harmless for possible handlers.
  711.  
  712.     if (aHandlerApp instanceof Ci.nsILocalHandlerApp) {
  713.       this._setLiteral(aHandlerAppID, NC_PATH, aHandlerApp.executable.path);
  714.       this._removeTarget(aHandlerAppID, NC_URI_TEMPLATE);
  715.     }
  716.     else {
  717.       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
  718.       this._setLiteral(aHandlerAppID, NC_URI_TEMPLATE, aHandlerApp.uriTemplate);
  719.       this._removeTarget(aHandlerAppID, NC_PATH);
  720.     }
  721.   },
  722.  
  723.   _storeAlwaysAsk: function HS__storeAlwaysAsk(aHandlerInfo) {
  724.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  725.     this._setLiteral(infoID,
  726.                      NC_ALWAYS_ASK,
  727.                      aHandlerInfo.alwaysAskBeforeHandling ? "true" : "false");
  728.   },
  729.  
  730.  
  731.   //**************************************************************************//
  732.   // Convenience Getters
  733.  
  734.   // Observer Service
  735.   __observerSvc: null,
  736.   get _observerSvc() {
  737.     if (!this.__observerSvc)
  738.       this.__observerSvc =
  739.         Cc["@mozilla.org/observer-service;1"].
  740.         getService(Ci.nsIObserverService);
  741.     return this.__observerSvc;
  742.   },
  743.  
  744.   // Directory Service
  745.   __dirSvc: null,
  746.   get _dirSvc() {
  747.     if (!this.__dirSvc)
  748.       this.__dirSvc =
  749.         Cc["@mozilla.org/file/directory_service;1"].
  750.         getService(Ci.nsIProperties);
  751.     return this.__dirSvc;
  752.   },
  753.  
  754.   // MIME Service
  755.   __mimeSvc: null,
  756.   get _mimeSvc() {
  757.     if (!this.__mimeSvc)
  758.       this.__mimeSvc =
  759.         Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
  760.         getService(Ci.nsIMIMEService);
  761.     return this.__mimeSvc;
  762.   },
  763.  
  764.   // Protocol Service
  765.   __protocolSvc: null,
  766.   get _protocolSvc() {
  767.     if (!this.__protocolSvc)
  768.       this.__protocolSvc =
  769.         Cc["@mozilla.org/uriloader/external-protocol-service;1"].
  770.         getService(Ci.nsIExternalProtocolService);
  771.     return this.__protocolSvc;
  772.   },
  773.  
  774.   // RDF Service
  775.   __rdf: null,
  776.   get _rdf() {
  777.     if (!this.__rdf)
  778.       this.__rdf = Cc["@mozilla.org/rdf/rdf-service;1"].
  779.                    getService(Ci.nsIRDFService);
  780.     return this.__rdf;
  781.   },
  782.  
  783.   // RDF Container Utils
  784.   __containerUtils: null,
  785.   get _containerUtils() {
  786.     if (!this.__containerUtils)
  787.       this.__containerUtils = Cc["@mozilla.org/rdf/container-utils;1"].
  788.                               getService(Ci.nsIRDFContainerUtils);
  789.     return this.__containerUtils;
  790.   },
  791.  
  792.   // RDF datasource containing content handling config (i.e. mimeTypes.rdf)
  793.   __ds: null,
  794.   get _ds() {
  795.     if (!this.__ds) {
  796.       var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
  797.       // FIXME: make this a memoizing getter if we use it anywhere else.
  798.       var ioService = Cc["@mozilla.org/network/io-service;1"].
  799.                       getService(Ci.nsIIOService);
  800.       var fileHandler = ioService.getProtocolHandler("file").
  801.                         QueryInterface(Ci.nsIFileProtocolHandler);
  802.       this.__ds =
  803.         this._rdf.GetDataSourceBlocking(fileHandler.getURLSpecFromFile(file));
  804.     }
  805.  
  806.     return this.__ds;
  807.   },
  808.  
  809.  
  810.   //**************************************************************************//
  811.   // Datastore Utils
  812.  
  813.   /**
  814.    * Get the string identifying whether this is a MIME or a protocol handler.
  815.    * This string is used in the URI IDs of various RDF properties.
  816.    * 
  817.    * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the class
  818.    * 
  819.    * @returns {string} the class
  820.    */
  821.   _getClass: function HS__getClass(aHandlerInfo) {
  822.     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
  823.       return CLASS_MIMEINFO;
  824.     else
  825.       return CLASS_PROTOCOLINFO;
  826.   },
  827.  
  828.   /**
  829.    * Return the unique identifier for a content type record, which stores
  830.    * the value field plus a reference to the content type's handler info record.
  831.    *
  832.    * |urn:<class>:<type>|
  833.    *
  834.    * XXX: should this be a property of nsIHandlerInfo?
  835.    *
  836.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  837.    * @param aType  {string} the type (a MIME type or protocol scheme)
  838.    *
  839.    * @returns {string} the ID
  840.    */
  841.   _getTypeID: function HS__getTypeID(aClass, aType) {
  842.     return "urn:" + aClass + ":" + aType;
  843.   },
  844.  
  845.   /**
  846.    * Return the unique identifier for a handler info record, which stores
  847.    * the preferredAction and alwaysAsk fields plus a reference to the preferred
  848.    * handler app.  Roughly equivalent to the nsIHandlerInfo interface.
  849.    *
  850.    * |urn:<class>:handler:<type>|
  851.    *
  852.    * FIXME: the type info record should be merged into the type record,
  853.    * since there's a one to one relationship between them, and this record
  854.    * merely stores additional attributes of a content type.
  855.    *
  856.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  857.    * @param aType  {string} the type (a MIME type or protocol scheme)
  858.    *
  859.    * @returns {string} the ID
  860.    */
  861.   _getInfoID: function HS__getInfoID(aClass, aType) {
  862.     return "urn:" + aClass + ":handler:" + aType;
  863.   },
  864.  
  865.   /**
  866.    * Return the unique identifier for a preferred handler record, which stores
  867.    * information about the preferred handler for a given content type, including
  868.    * its human-readable name and the path to its executable (for a local app)
  869.    * or its URI template (for a web app).
  870.    * 
  871.    * |urn:<class>:externalApplication:<type>|
  872.    *
  873.    * XXX: should this be a property of nsIHandlerApp?
  874.    *
  875.    * FIXME: this should be an arbitrary ID, and we should retrieve it from
  876.    * the datastore for a given content type via the NC:ExternalApplication
  877.    * property rather than looking for a specific ID, so a handler doesn't
  878.    * have to change IDs when it goes from being a possible handler to being
  879.    * the preferred one (once we support possible handlers).
  880.    * 
  881.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  882.    * @param aType  {string} the type (a MIME type or protocol scheme)
  883.    * 
  884.    * @returns {string} the ID
  885.    */
  886.   _getPreferredHandlerID: function HS__getPreferredHandlerID(aClass, aType) {
  887.     return "urn:" + aClass + ":externalApplication:" + aType;
  888.   },
  889.  
  890.   /**
  891.    * Return the unique identifier for a handler app record, which stores
  892.    * information about a possible handler for one or more content types,
  893.    * including its human-readable name and the path to its executable (for a
  894.    * local app) or its URI template (for a web app).
  895.    *
  896.    * Note: handler app IDs for preferred handlers are different.  For those,
  897.    * see the _getPreferredHandlerID method.
  898.    *
  899.    * @param aHandlerApp  {nsIHandlerApp}   the handler app object
  900.    */
  901.   _getPossibleHandlerAppID: function HS__getPossibleHandlerAppID(aHandlerApp) {
  902.     var handlerAppID = "urn:handler:";
  903.  
  904.     if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
  905.       handlerAppID += "local:" + aHandlerApp.executable.path;
  906.     else {
  907.       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
  908.       handlerAppID += "web:" + aHandlerApp.uriTemplate;
  909.     }
  910.  
  911.     return handlerAppID;
  912.   },
  913.  
  914.   /**
  915.    * Get the list of types for the given class, creating the list if it doesn't
  916.    * already exist. The class can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO
  917.    * (i.e. the result of a call to _getClass).
  918.    * 
  919.    * |urn:<class>s|
  920.    * |urn:<class>s:root|
  921.    * 
  922.    * @param aClass {string} the class for which to retrieve a list of types
  923.    *
  924.    * @returns {nsIRDFContainer} the list of types
  925.    */
  926.   _ensureAndGetTypeList: function HS__ensureAndGetTypeList(aClass) {
  927.     var source = this._rdf.GetResource("urn:" + aClass + "s");
  928.     var property =
  929.       this._rdf.GetResource(aClass == CLASS_MIMEINFO ? NC_MIME_TYPES
  930.                                                      : NC_PROTOCOL_SCHEMES);
  931.     var target = this._rdf.GetResource("urn:" + aClass + "s:root");
  932.  
  933.     // Make sure we have an arc from the source to the target.
  934.     if (!this._ds.HasAssertion(source, property, target, true))
  935.       this._ds.Assert(source, property, target, true);
  936.  
  937.     // Make sure the target is a container.
  938.     if (!this._containerUtils.IsContainer(this._ds, target))
  939.       this._containerUtils.MakeSeq(this._ds, target);
  940.  
  941.     // Get the type list as an RDF container.
  942.     var typeList = Cc["@mozilla.org/rdf/container;1"].
  943.                    createInstance(Ci.nsIRDFContainer);
  944.     typeList.Init(this._ds, target);
  945.  
  946.     return typeList;
  947.   },
  948.  
  949.   /**
  950.    * Make sure there are records in the datasource for the given content type
  951.    * by creating them if they don't already exist.  We have to do this before
  952.    * storing any specific data, because we can't assume the presence
  953.    * of the records (the nsIHandlerInfo object might have been created
  954.    * from the OS), and the records have to all be there in order for the helper
  955.    * app service to properly construct an nsIHandlerInfo object for the type.
  956.    *
  957.    * Based on old downloadactions.js::_ensureMIMERegistryEntry.
  958.    *
  959.    * @param aHandlerInfo {nsIHandlerInfo} the type to make sure has a record
  960.    */
  961.   _ensureRecordsForType: function HS__ensureRecordsForType(aHandlerInfo) {
  962.     // Get the list of types.
  963.     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
  964.  
  965.     // If there's already a record in the datastore for this type, then we
  966.     // don't need to do anything more.
  967.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  968.     var type = this._rdf.GetResource(typeID);
  969.     if (typeList.IndexOf(type) != -1)
  970.       return;
  971.  
  972.     // Create a basic type record for this type.
  973.     typeList.AppendElement(type);
  974.     this._setLiteral(typeID, NC_VALUE, aHandlerInfo.type);
  975.     
  976.     // Create a basic info record for this type.
  977.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  978.     this._setLiteral(infoID, NC_ALWAYS_ASK, "false");
  979.     this._setResource(typeID, NC_HANDLER_INFO, infoID);
  980.     // XXX Shouldn't we set preferredAction to useSystemDefault?
  981.     // That's what it is if there's no record in the datastore; why should it
  982.     // change to useHelperApp just because we add a record to the datastore?
  983.     
  984.     // Create a basic preferred handler record for this type.
  985.     // XXX Not sure this is necessary, since preferred handlers are optional,
  986.     // and nsExternalHelperAppService::FillHandlerInfoForTypeFromDS doesn't seem
  987.     // to require the record , but downloadactions.js::_ensureMIMERegistryEntry
  988.     // used to create it, so we'll do the same.
  989.     var preferredHandlerID =
  990.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  991.     this._setLiteral(preferredHandlerID, NC_PATH, "");
  992.     this._setResource(infoID, NC_PREFERRED_APP, preferredHandlerID);
  993.   },
  994.  
  995.   /**
  996.    * Append known handlers of the given class to the given array.  The class
  997.    * can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO.
  998.    *
  999.    * @param aHandlers   {array} the array of handlers to append to
  1000.    * @param aClass      {string} the class for which to append handlers
  1001.    */
  1002.   _appendHandlers: function HS__appendHandlers(aHandlers, aClass) {
  1003.     var typeList = this._ensureAndGetTypeList(aClass);
  1004.     var enumerator = typeList.GetElements();
  1005.  
  1006.     while (enumerator.hasMoreElements()) {
  1007.       var element = enumerator.getNext();
  1008.       
  1009.       // This should never happen.  If it does, that means our datasource
  1010.       // is corrupted with type list entries that point to literal values
  1011.       // instead of resources.  If it does happen, let's just do our best
  1012.       // to recover by ignoring this entry and moving on to the next one.
  1013.       if (!(element instanceof Ci.nsIRDFResource))
  1014.         continue;
  1015.  
  1016.       // Get the value of the element's NC:value property, which contains
  1017.       // the MIME type or scheme for which we're retrieving a handler info.
  1018.       var type = this._getValue(element.ValueUTF8, NC_VALUE);
  1019.       if (!type)
  1020.         continue;
  1021.  
  1022.       var handler;
  1023.       if (typeList.Resource.ValueUTF8 == "urn:mimetypes:root")
  1024.         handler = this._mimeSvc.getFromTypeAndExtension(type, null);
  1025.       else
  1026.         handler = this._protocolSvc.getProtocolHandlerInfo(type);
  1027.  
  1028.       aHandlers.appendElement(handler, false);
  1029.     }
  1030.   },
  1031.  
  1032.   /**
  1033.    * Whether or not a property of an RDF source has a value.
  1034.    *
  1035.    * @param sourceURI   {string}  the URI of the source
  1036.    * @param propertyURI {string}  the URI of the property
  1037.    * @returns           {boolean} whether or not the property has a value
  1038.    */
  1039.   _hasValue: function HS__hasValue(sourceURI, propertyURI) {
  1040.     var source = this._rdf.GetResource(sourceURI);
  1041.     var property = this._rdf.GetResource(propertyURI);
  1042.     return this._ds.hasArcOut(source, property);
  1043.   },
  1044.  
  1045.   /**
  1046.    * Get the value of a property of an RDF source.
  1047.    *
  1048.    * @param sourceURI   {string} the URI of the source
  1049.    * @param propertyURI {string} the URI of the property
  1050.    * @returns           {string} the value of the property
  1051.    */
  1052.   _getValue: function HS__getValue(sourceURI, propertyURI) {
  1053.     var source = this._rdf.GetResource(sourceURI);
  1054.     var property = this._rdf.GetResource(propertyURI);
  1055.  
  1056.     var target = this._ds.GetTarget(source, property, true);
  1057.  
  1058.     if (!target)
  1059.       return null;
  1060.     
  1061.     if (target instanceof Ci.nsIRDFResource)
  1062.       return target.ValueUTF8;
  1063.  
  1064.     if (target instanceof Ci.nsIRDFLiteral)
  1065.       return target.Value;
  1066.  
  1067.     return null;
  1068.   },
  1069.  
  1070.   /**
  1071.    * Get all targets for the property of an RDF source.
  1072.    *
  1073.    * @param sourceURI   {string} the URI of the source
  1074.    * @param propertyURI {string} the URI of the property
  1075.    * 
  1076.    * @returns {nsISimpleEnumerator} an enumerator of targets
  1077.    */
  1078.   _getTargets: function HS__getTargets(sourceURI, propertyURI) {
  1079.     var source = this._rdf.GetResource(sourceURI);
  1080.     var property = this._rdf.GetResource(propertyURI);
  1081.  
  1082.     return this._ds.GetTargets(source, property, true);
  1083.   },
  1084.  
  1085.   /**
  1086.    * Set a property of an RDF source to a literal value.
  1087.    *
  1088.    * @param sourceURI   {string} the URI of the source
  1089.    * @param propertyURI {string} the URI of the property
  1090.    * @param value       {string} the literal value
  1091.    */
  1092.   _setLiteral: function HS__setLiteral(sourceURI, propertyURI, value) {
  1093.     var source = this._rdf.GetResource(sourceURI);
  1094.     var property = this._rdf.GetResource(propertyURI);
  1095.     var target = this._rdf.GetLiteral(value);
  1096.     
  1097.     this._setTarget(source, property, target);
  1098.   },
  1099.  
  1100.   /**
  1101.    * Set a property of an RDF source to a resource target.
  1102.    *
  1103.    * @param sourceURI   {string} the URI of the source
  1104.    * @param propertyURI {string} the URI of the property
  1105.    * @param targetURI   {string} the URI of the target
  1106.    */
  1107.   _setResource: function HS__setResource(sourceURI, propertyURI, targetURI) {
  1108.     var source = this._rdf.GetResource(sourceURI);
  1109.     var property = this._rdf.GetResource(propertyURI);
  1110.     var target = this._rdf.GetResource(targetURI);
  1111.     
  1112.     this._setTarget(source, property, target);
  1113.   },
  1114.  
  1115.   /**
  1116.    * Assert an arc into the RDF datasource if there is no arc with the given
  1117.    * source and property; otherwise, if there is already an existing arc,
  1118.    * change it to point to the given target. _setLiteral and _setResource
  1119.    * call this after converting their string arguments into resources
  1120.    * and literals, and most callers should call one of those two methods
  1121.    * instead of this one.
  1122.    *
  1123.    * @param source    {nsIRDFResource}  the source
  1124.    * @param property  {nsIRDFResource}  the property
  1125.    * @param target    {nsIRDFNode}      the target
  1126.    */
  1127.   _setTarget: function HS__setTarget(source, property, target) {
  1128.     if (this._ds.hasArcOut(source, property)) {
  1129.       var oldTarget = this._ds.GetTarget(source, property, true);
  1130.       this._ds.Change(source, property, oldTarget, target);
  1131.     }
  1132.     else
  1133.       this._ds.Assert(source, property, target, true);
  1134.   },
  1135.  
  1136.   /**
  1137.    * Assert that a property of an RDF source has a resource target.
  1138.    * 
  1139.    * The difference between this method and _setResource is that this one adds
  1140.    * an assertion even if one already exists, which allows its callers to make
  1141.    * sets of assertions (i.e. to set a property to multiple targets).
  1142.    *
  1143.    * @param sourceURI   {string} the URI of the source
  1144.    * @param propertyURI {string} the URI of the property
  1145.    * @param targetURI   {string} the URI of the target
  1146.    */
  1147.   _addResourceTarget: function HS__addResourceTarget(sourceURI, propertyURI,
  1148.                                                      targetURI) {
  1149.     var source = this._rdf.GetResource(sourceURI);
  1150.     var property = this._rdf.GetResource(propertyURI);
  1151.     var target = this._rdf.GetResource(targetURI);
  1152.     
  1153.     this._ds.Assert(source, property, target, true);
  1154.   },
  1155.  
  1156.   /**
  1157.    * Whether or not a property of an RDF source has a given resource target.
  1158.    * 
  1159.    * @param sourceURI   {string} the URI of the source
  1160.    * @param propertyURI {string} the URI of the property
  1161.    * @param targetURI   {string} the URI of the target
  1162.    *
  1163.    * @returns {boolean} whether or not there is such an assertion
  1164.    */
  1165.   _hasResourceAssertion: function HS__hasResourceAssertion(sourceURI,
  1166.                                                            propertyURI,
  1167.                                                            targetURI) {
  1168.     var source = this._rdf.GetResource(sourceURI);
  1169.     var property = this._rdf.GetResource(propertyURI);
  1170.     var target = this._rdf.GetResource(targetURI);
  1171.  
  1172.     return this._ds.HasAssertion(source, property, target, true);
  1173.   },
  1174.  
  1175.   /**
  1176.    * Whether or not a property of an RDF source has a given literal value.
  1177.    * 
  1178.    * @param sourceURI   {string} the URI of the source
  1179.    * @param propertyURI {string} the URI of the property
  1180.    * @param value       {string} the literal value
  1181.    *
  1182.    * @returns {boolean} whether or not there is such an assertion
  1183.    */
  1184.   _hasLiteralAssertion: function HS__hasLiteralAssertion(sourceURI,
  1185.                                                          propertyURI,
  1186.                                                          value) {
  1187.     var source = this._rdf.GetResource(sourceURI);
  1188.     var property = this._rdf.GetResource(propertyURI);
  1189.     var target = this._rdf.GetLiteral(value);
  1190.  
  1191.     return this._ds.HasAssertion(source, property, target, true);
  1192.   },
  1193.  
  1194.   /**
  1195.    * Whether or not there is an RDF source that has the given property set to
  1196.    * the given literal value.
  1197.    * 
  1198.    * @param propertyURI {string} the URI of the property
  1199.    * @param value       {string} the literal value
  1200.    *
  1201.    * @returns {boolean} whether or not there is a source
  1202.    */
  1203.   _existsLiteralTarget: function HS__existsLiteralTarget(propertyURI, value) {
  1204.     var property = this._rdf.GetResource(propertyURI);
  1205.     var target = this._rdf.GetLiteral(value);
  1206.  
  1207.     return this._ds.hasArcIn(target, property);
  1208.   },
  1209.  
  1210.   /**
  1211.    * Get the source for a property set to a given literal value.
  1212.    *
  1213.    * @param propertyURI {string} the URI of the property
  1214.    * @param value       {string} the literal value
  1215.    */
  1216.   _getSourceForLiteral: function HS__getSourceForLiteral(propertyURI, value) {
  1217.     var property = this._rdf.GetResource(propertyURI);
  1218.     var target = this._rdf.GetLiteral(value);
  1219.  
  1220.     var source = this._ds.GetSource(property, target, true);
  1221.     if (source)
  1222.       return source.ValueUTF8;
  1223.  
  1224.     return null;
  1225.   },
  1226.  
  1227.   /**
  1228.    * Whether or not there is an RDF source that has the given property set to
  1229.    * the given resource target.
  1230.    * 
  1231.    * @param propertyURI {string} the URI of the property
  1232.    * @param targetURI   {string} the URI of the target
  1233.    *
  1234.    * @returns {boolean} whether or not there is a source
  1235.    */
  1236.   _existsResourceTarget: function HS__existsResourceTarget(propertyURI,
  1237.                                                            targetURI) {
  1238.     var property = this._rdf.GetResource(propertyURI);
  1239.     var target = this._rdf.GetResource(targetURI);
  1240.  
  1241.     return this._ds.hasArcIn(target, property);
  1242.   },
  1243.  
  1244.   /**
  1245.    * Remove a property of an RDF source.
  1246.    *
  1247.    * @param sourceURI   {string} the URI of the source
  1248.    * @param propertyURI {string} the URI of the property
  1249.    */
  1250.   _removeTarget: function HS__removeTarget(sourceURI, propertyURI) {
  1251.     var source = this._rdf.GetResource(sourceURI);
  1252.     var property = this._rdf.GetResource(propertyURI);
  1253.  
  1254.     if (this._ds.hasArcOut(source, property)) {
  1255.       var target = this._ds.GetTarget(source, property, true);
  1256.       this._ds.Unassert(source, property, target);
  1257.     }
  1258.   },
  1259.  
  1260.  /**
  1261.   * Remove all assertions about a given RDF source.
  1262.   *
  1263.   * Note: not recursive.  If some assertions point to other resources,
  1264.   * and you want to remove assertions about those resources too, you need
  1265.   * to do so manually.
  1266.   *
  1267.   * @param sourceURI {string} the URI of the source
  1268.   */
  1269.   _removeAssertions: function HS__removeAssertions(sourceURI) {
  1270.     var source = this._rdf.GetResource(sourceURI);
  1271.     var properties = this._ds.ArcLabelsOut(source);
  1272.  
  1273.     while (properties.hasMoreElements()) {
  1274.       let property = properties.getNext();
  1275.       let targets = this._ds.GetTargets(source, property, true);
  1276.       while (targets.hasMoreElements()) {
  1277.         let target = targets.getNext();
  1278.         this._ds.Unassert(source, property, target);
  1279.       }
  1280.     }
  1281.   }
  1282.  
  1283. };
  1284.  
  1285.  
  1286. //****************************************************************************//
  1287. // More XPCOM Plumbing
  1288.  
  1289. function NSGetModule(compMgr, fileSpec) {
  1290.   return XPCOMUtils.generateModule([HandlerService]);
  1291. }
  1292.