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