1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
14 * The Original Code is the Mozilla browser.
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.
21 * Myk Melez <myk@mozilla.org>
22 * Dan Mosedale <dmose@mozilla.org>
23 * Florian Queze <florian@queze.net>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 const Ci
= Components
.interfaces
;
40 const Cc
= Components
.classes
;
41 const Cu
= Components
.utils
;
42 const Cr
= Components
.results
;
45 const CLASS_MIMEINFO
= "mimetype";
46 const CLASS_PROTOCOLINFO
= "scheme";
50 const NC_NS
= "http://home.netscape.com/NC-rdf#";
52 // the most recent default handlers that have been injected. Note that
53 // this is used to construct an RDF resource, which needs to have NC_NS
54 // prepended, since that hasn't been done yet
55 const DEFAULT_HANDLERS_VERSION
= "defaultHandlersVersion";
57 // type list properties
59 const NC_MIME_TYPES
= NC_NS
+ "MIME-types";
60 const NC_PROTOCOL_SCHEMES
= NC_NS
+ "Protocol-Schemes";
62 // content type ("type") properties
64 // nsIHandlerInfo::type
65 const NC_VALUE
= NC_NS
+ "value";
66 const NC_DESCRIPTION
= NC_NS
+ "description";
68 // additional extensions
69 const NC_FILE_EXTENSIONS
= NC_NS
+ "fileExtensions";
71 // references nsIHandlerInfo record
72 const NC_HANDLER_INFO
= NC_NS
+ "handlerProp";
74 // handler info ("info") properties
76 // nsIHandlerInfo::preferredAction
77 const NC_SAVE_TO_DISK
= NC_NS
+ "saveToDisk";
78 const NC_HANDLE_INTERNALLY
= NC_NS
+ "handleInternal";
79 const NC_USE_SYSTEM_DEFAULT
= NC_NS
+ "useSystemDefault";
81 // nsIHandlerInfo::alwaysAskBeforeHandling
82 const NC_ALWAYS_ASK
= NC_NS
+ "alwaysAsk";
84 // references nsIHandlerApp records
85 const NC_PREFERRED_APP
= NC_NS
+ "externalApplication";
86 const NC_POSSIBLE_APP
= NC_NS
+ "possibleApplication";
88 // handler app ("handler") properties
90 // nsIHandlerApp::name
91 const NC_PRETTY_NAME
= NC_NS
+ "prettyName";
93 // nsILocalHandlerApp::executable
94 const NC_PATH
= NC_NS
+ "path";
96 // nsIWebHandlerApp::uriTemplate
97 const NC_URI_TEMPLATE
= NC_NS
+ "uriTemplate";
99 // nsIDBusHandlerApp::service
100 const NC_SERVICE
= NC_NS
+ "service";
102 // nsIDBusHandlerApp::method
103 const NC_METHOD
= NC_NS
+ "method";
105 // nsIDBusHandlerApp::objectPath
106 const NC_OBJPATH
= NC_NS
+ "objectPath";
108 // nsIDBusHandlerApp::dbusInterface
109 const NC_INTERFACE
= NC_NS
+ "dBusInterface";
111 Cu
.import("resource://gre/modules/XPCOMUtils.jsm");
114 function HandlerService() {
118 HandlerService
.prototype = {
119 //**************************************************************************//
122 classDescription
: "Handler Service",
123 classID
: Components
.ID("{32314cc8-22f7-4f7f-a645-1a45453ba6a6}"),
124 contractID
: "@mozilla.org/uriloader/handler-service;1",
125 QueryInterface
: XPCOMUtils
.generateQI([Ci
.nsIHandlerService
]),
128 //**************************************************************************//
129 // Initialization & Destruction
131 _init
: function HS__init() {
132 // Observe profile-before-change so we can switch to the datasource
133 // in the new profile when the user changes profiles.
134 this._observerSvc
.addObserver(this, "profile-before-change", false);
136 // Observe xpcom-shutdown so we can remove these observers
137 // when the application shuts down.
138 this._observerSvc
.addObserver(this, "xpcom-shutdown", false);
140 // Observe profile-do-change so that non-default profiles get upgraded too
141 this._observerSvc
.addObserver(this, "profile-do-change", false);
143 // do any necessary updating of the datastore
147 _updateDB
: function HS__updateDB() {
149 var defaultHandlersVersion
= this._datastoreDefaultHandlersVersion
;
151 // accessing the datastore failed, we can't update anything
156 // if we don't have the current version of the default prefs for
157 // this locale, inject any new default handers into the datastore
158 if (defaultHandlersVersion
< this._prefsDefaultHandlersVersion
) {
160 // set the new version first so that if we recurse we don't
161 // call _injectNewDefaults several times
162 this._datastoreDefaultHandlersVersion
=
163 this._prefsDefaultHandlersVersion
;
164 this._injectNewDefaults();
167 // if injecting the defaults failed, set the version back to the
169 this._datastoreDefaultHandlersVersion
= defaultHandlersVersion
;
173 get _currentLocale() {
174 var chromeRegistry
= Cc
["@mozilla.org/chrome/chrome-registry;1"].
175 getService(Ci
.nsIXULChromeRegistry
);
176 var currentLocale
= chromeRegistry
.getSelectedLocale("global");
177 return currentLocale
;
180 _destroy
: function HS__destroy() {
181 this._observerSvc
.removeObserver(this, "profile-before-change");
182 this._observerSvc
.removeObserver(this, "xpcom-shutdown");
183 this._observerSvc
.removeObserver(this, "profile-do-change");
185 // XXX Should we also null references to all the services that get stored
186 // by our memoizing getters in the Convenience Getters section?
189 _onProfileChange
: function HS__onProfileChange() {
190 // Lose our reference to the datasource so we reacquire it
191 // from the new profile the next time we need it.
195 _isInHandlerArray
: function HS__isInHandlerArray(aArray
, aHandler
) {
196 var enumerator
= aArray
.enumerate();
197 while (enumerator
.hasMoreElements()) {
198 let handler
= enumerator
.getNext();
199 handler
.QueryInterface(Ci
.nsIHandlerApp
);
200 if (handler
.equals(aHandler
))
207 // note that this applies to the current locale only
208 get _datastoreDefaultHandlersVersion() {
209 var version
= this._getValue("urn:root", NC_NS
+ this._currentLocale
+
210 "_" + DEFAULT_HANDLERS_VERSION
);
212 return version
? version
: -1;
215 set _datastoreDefaultHandlersVersion(aNewVersion
) {
216 return this._setLiteral("urn:root", NC_NS
+ this._currentLocale
+ "_" +
217 DEFAULT_HANDLERS_VERSION
, aNewVersion
);
220 get _prefsDefaultHandlersVersion() {
221 // get handler service pref branch
222 var prefSvc
= Cc
["@mozilla.org/preferences-service;1"].
223 getService(Ci
.nsIPrefService
);
224 var handlerSvcBranch
= prefSvc
.getBranch("gecko.handlerService.");
226 // get the version of the preferences for this locale
227 return Number(handlerSvcBranch
.
228 getComplexValue("defaultHandlersVersion",
229 Ci
.nsIPrefLocalizedString
).data
);
232 _injectNewDefaults
: function HS__injectNewDefaults() {
233 // get handler service pref branch
234 var prefSvc
= Cc
["@mozilla.org/preferences-service;1"].
235 getService(Ci
.nsIPrefService
);
237 let schemesPrefBranch
= prefSvc
.getBranch("gecko.handlerService.schemes.");
238 let schemePrefList
= schemesPrefBranch
.getChildList("", {});
242 // read all the scheme prefs into a hash
243 for each (var schemePrefName
in schemePrefList
) {
245 let [scheme
, handlerNumber
, attribute
] = schemePrefName
.split(".");
249 schemesPrefBranch
.getComplexValue(schemePrefName
,
250 Ci
.nsIPrefLocalizedString
).data
;
251 if (!(scheme
in schemes
))
252 schemes
[scheme
] = {};
254 if (!(handlerNumber
in schemes
[scheme
]))
255 schemes
[scheme
][handlerNumber
] = {};
257 schemes
[scheme
][handlerNumber
][attribute
] = attrData
;
261 let protoSvc
= Cc
["@mozilla.org/uriloader/external-protocol-service;1"].
262 getService(Ci
.nsIExternalProtocolService
);
263 for (var scheme
in schemes
) {
265 // This clause is essentially a reimplementation of
266 // nsIExternalProtocolHandlerService.getProtocolHandlerInfo().
267 // Necessary because calling that from here would make XPConnect barf
268 // when getService tried to re-enter the constructor for this
270 let osDefaultHandlerFound
= {};
271 let protoInfo
= protoSvc
.getProtocolHandlerInfoFromOS(scheme
,
272 osDefaultHandlerFound
);
274 if (this.exists(protoInfo
))
275 this.fillHandlerInfo(protoInfo
, null);
277 protoSvc
.setProtocolHandlerDefaults(protoInfo
,
278 osDefaultHandlerFound
.value
);
280 // cache the possible handlers to avoid extra xpconnect traversals.
281 let possibleHandlers
= protoInfo
.possibleApplicationHandlers
;
283 for each (var handlerPrefs
in schemes
[scheme
]) {
285 let handlerApp
= Cc
["@mozilla.org/uriloader/web-handler-app;1"].
286 createInstance(Ci
.nsIWebHandlerApp
);
288 handlerApp
.uriTemplate
= handlerPrefs
.uriTemplate
;
289 handlerApp
.name
= handlerPrefs
.name
;
291 if (!this._isInHandlerArray(possibleHandlers
, handlerApp
)) {
292 possibleHandlers
.appendElement(handlerApp
, false);
296 this.store(protoInfo
);
300 //**************************************************************************//
303 observe
: function HS__observe(subject
, topic
, data
) {
305 case "profile-before-change":
306 this._onProfileChange();
308 case "xpcom-shutdown":
311 case "profile-do-change":
318 //**************************************************************************//
321 enumerate
: function HS_enumerate() {
322 var handlers
= Cc
["@mozilla.org/array;1"].
323 createInstance(Ci
.nsIMutableArray
);
324 this._appendHandlers(handlers
, CLASS_MIMEINFO
);
325 this._appendHandlers(handlers
, CLASS_PROTOCOLINFO
);
326 return handlers
.enumerate();
329 fillHandlerInfo
: function HS_fillHandlerInfo(aHandlerInfo
, aOverrideType
) {
330 var type
= aOverrideType
|| aHandlerInfo
.type
;
331 var typeID
= this._getTypeID(this._getClass(aHandlerInfo
), type
);
333 // Determine whether or not information about this handler is available
334 // in the datastore by looking for its "value" property, which stores its
335 // type and should always be present.
336 if (!this._hasValue(typeID
, NC_VALUE
))
337 throw Cr
.NS_ERROR_NOT_AVAILABLE
;
339 // Retrieve the human-readable description of the type.
340 if (this._hasValue(typeID
, NC_DESCRIPTION
))
341 aHandlerInfo
.description
= this._getValue(typeID
, NC_DESCRIPTION
);
343 // Note: for historical reasons, we don't actually check that the type
344 // record has a "handlerProp" property referencing the info record. It's
345 // unclear whether or not we should start doing this check; perhaps some
346 // legacy datasources don't have such references.
347 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), type
);
349 aHandlerInfo
.preferredAction
= this._retrievePreferredAction(infoID
);
351 var preferredHandlerID
=
352 this._getPreferredHandlerID(this._getClass(aHandlerInfo
), type
);
354 // Retrieve the preferred handler.
355 // Note: for historical reasons, we don't actually check that the info
356 // record has an "externalApplication" property referencing the preferred
357 // handler record. It's unclear whether or not we should start doing
358 // this check; perhaps some legacy datasources don't have such references.
359 aHandlerInfo
.preferredApplicationHandler
=
360 this._retrieveHandlerApp(preferredHandlerID
);
362 // Fill the array of possible handlers with the ones in the datastore.
363 this._fillPossibleHandlers(infoID
,
364 aHandlerInfo
.possibleApplicationHandlers
,
365 aHandlerInfo
.preferredApplicationHandler
);
367 // If we have an "always ask" flag stored in the RDF, always use its
368 // value. Otherwise, use the default value stored in the pref service.
370 if (this._hasValue(infoID
, NC_ALWAYS_ASK
)) {
371 alwaysAsk
= (this._getValue(infoID
, NC_ALWAYS_ASK
) != "false");
373 var prefSvc
= Cc
["@mozilla.org/preferences-service;1"].
374 getService(Ci
.nsIPrefService
);
375 var prefBranch
= prefSvc
.getBranch("network.protocol-handler.");
377 alwaysAsk
= prefBranch
.getBoolPref("warn-external." + type
);
379 // will throw if pref didn't exist.
381 alwaysAsk
= prefBranch
.getBoolPref("warn-external-default");
383 // Nothing to tell us what to do, so be paranoid and prompt.
388 aHandlerInfo
.alwaysAskBeforeHandling
= alwaysAsk
;
390 // If the object represents a MIME type handler, then also retrieve
391 // any file extensions.
392 if (aHandlerInfo
instanceof Ci
.nsIMIMEInfo
)
393 for each (let fileExtension
in this._retrieveFileExtensions(typeID
))
394 aHandlerInfo
.appendExtension(fileExtension
);
397 store
: function HS_store(aHandlerInfo
) {
398 // FIXME: when we switch from RDF to something with transactions (like
399 // SQLite), enclose the following changes in a transaction so they all
400 // get rolled back if any of them fail and we don't leave the datastore
401 // in an inconsistent state.
403 this._ensureRecordsForType(aHandlerInfo
);
404 this._storePreferredAction(aHandlerInfo
);
405 this._storePreferredHandler(aHandlerInfo
);
406 this._storePossibleHandlers(aHandlerInfo
);
407 this._storeAlwaysAsk(aHandlerInfo
);
409 // Write the changes to the database immediately so we don't lose them
410 // if the application crashes.
411 if (this._ds
instanceof Ci
.nsIRDFRemoteDataSource
)
415 exists
: function HS_exists(aHandlerInfo
) {
419 var typeID
= this._getTypeID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
420 found
= this._hasLiteralAssertion(typeID
, NC_VALUE
, aHandlerInfo
.type
);
422 // If the RDF threw (eg, corrupt file), treat as non-existent.
429 remove
: function HS_remove(aHandlerInfo
) {
430 var preferredHandlerID
=
431 this._getPreferredHandlerID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
432 this._removeAssertions(preferredHandlerID
);
434 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
436 // Get a list of possible handlers. After we have removed the info record,
437 // we'll check if any other info records reference these handlers, and we'll
438 // remove the handler records that aren't referenced by other info records.
439 var possibleHandlerIDs
= [];
440 var possibleHandlerTargets
= this._getTargets(infoID
, NC_POSSIBLE_APP
);
441 while (possibleHandlerTargets
.hasMoreElements()) {
442 let possibleHandlerTarget
= possibleHandlerTargets
.getNext();
443 // Note: possibleHandlerTarget should always be an nsIRDFResource.
444 // The conditional is just here in case of a corrupt RDF datasource.
445 if (possibleHandlerTarget
instanceof Ci
.nsIRDFResource
)
446 possibleHandlerIDs
.push(possibleHandlerTarget
.ValueUTF8
);
449 // Remove the info record.
450 this._removeAssertions(infoID
);
452 // Now that we've removed the info record, remove any possible handlers
453 // that aren't referenced by other info records.
454 for each (let possibleHandlerID
in possibleHandlerIDs
)
455 if (!this._existsResourceTarget(NC_POSSIBLE_APP
, possibleHandlerID
))
456 this._removeAssertions(possibleHandlerID
);
458 var typeID
= this._getTypeID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
459 this._removeAssertions(typeID
);
461 // Now that there's no longer a handler for this type, remove the type
462 // from the list of types for which there are known handlers.
463 var typeList
= this._ensureAndGetTypeList(this._getClass(aHandlerInfo
));
464 var type
= this._rdf
.GetResource(typeID
);
465 var typeIndex
= typeList
.IndexOf(type
);
467 typeList
.RemoveElementAt(typeIndex
, true);
469 // Write the changes to the database immediately so we don't lose them
470 // if the application crashes.
471 // XXX If we're removing a bunch of handlers at once, will flushing
472 // after every removal cause a significant performance hit?
473 if (this._ds
instanceof Ci
.nsIRDFRemoteDataSource
)
477 getTypeFromExtension
: function HS_getTypeFromExtension(aFileExtension
) {
478 var fileExtension
= aFileExtension
.toLowerCase();
481 if (this._existsLiteralTarget(NC_FILE_EXTENSIONS
, fileExtension
))
482 typeID
= this._getSourceForLiteral(NC_FILE_EXTENSIONS
, fileExtension
);
484 if (typeID
&& this._hasValue(typeID
, NC_VALUE
)) {
485 let type
= this._getValue(typeID
, NC_VALUE
);
487 throw Cr
.NS_ERROR_FAILURE
;
495 //**************************************************************************//
499 * Retrieve the preferred action for the info record with the given ID.
501 * @param aInfoID {string} the info record ID
503 * @returns {integer} the preferred action enumeration value
505 _retrievePreferredAction
: function HS__retrievePreferredAction(aInfoID
) {
506 if (this._getValue(aInfoID
, NC_SAVE_TO_DISK
) == "true")
507 return Ci
.nsIHandlerInfo
.saveToDisk
;
509 if (this._getValue(aInfoID
, NC_USE_SYSTEM_DEFAULT
) == "true")
510 return Ci
.nsIHandlerInfo
.useSystemDefault
;
512 if (this._getValue(aInfoID
, NC_HANDLE_INTERNALLY
) == "true")
513 return Ci
.nsIHandlerInfo
.handleInternal
;
515 return Ci
.nsIHandlerInfo
.useHelperApp
;
519 * Fill an array of possible handlers with the handlers for the given info ID.
521 * @param aInfoID {string} the ID of the info record
522 * @param aPossibleHandlers {nsIMutableArray} the array of possible handlers
523 * @param aPreferredHandler {nsIHandlerApp} the preferred handler, if any
525 _fillPossibleHandlers
: function HS__fillPossibleHandlers(aInfoID
,
528 // The set of possible handlers should include the preferred handler,
529 // but legacy datastores (from before we added possible handlers) won't
530 // include the preferred handler, so check if it's included as we build
531 // the list of handlers, and, if it's not included, add it to the list.
532 if (aPreferredHandler
)
533 aPossibleHandlers
.appendElement(aPreferredHandler
, false);
535 var possibleHandlerTargets
= this._getTargets(aInfoID
, NC_POSSIBLE_APP
);
537 while (possibleHandlerTargets
.hasMoreElements()) {
538 let possibleHandlerTarget
= possibleHandlerTargets
.getNext();
539 if (!(possibleHandlerTarget
instanceof Ci
.nsIRDFResource
))
542 let possibleHandlerID
= possibleHandlerTarget
.ValueUTF8
;
543 let possibleHandler
= this._retrieveHandlerApp(possibleHandlerID
);
544 if (possibleHandler
&& (!aPreferredHandler
||
545 !possibleHandler
.equals(aPreferredHandler
)))
546 aPossibleHandlers
.appendElement(possibleHandler
, false);
551 * Retrieve the handler app object with the given ID.
553 * @param aHandlerAppID {string} the ID of the handler app to retrieve
555 * @returns {nsIHandlerApp} the handler app, if any; otherwise null
557 _retrieveHandlerApp
: function HS__retrieveHandlerApp(aHandlerAppID
) {
560 // If it has a path, it's a local handler; otherwise, it's a web handler.
561 if (this._hasValue(aHandlerAppID
, NC_PATH
)) {
563 this._getFileWithPath(this._getValue(aHandlerAppID
, NC_PATH
));
567 handlerApp
= Cc
["@mozilla.org/uriloader/local-handler-app;1"].
568 createInstance(Ci
.nsILocalHandlerApp
);
569 handlerApp
.executable
= executable
;
571 else if (this._hasValue(aHandlerAppID
, NC_URI_TEMPLATE
)) {
572 let uriTemplate
= this._getValue(aHandlerAppID
, NC_URI_TEMPLATE
);
576 handlerApp
= Cc
["@mozilla.org/uriloader/web-handler-app;1"].
577 createInstance(Ci
.nsIWebHandlerApp
);
578 handlerApp
.uriTemplate
= uriTemplate
;
580 else if (this._hasValue(aHandlerAppID
, NC_SERVICE
)) {
581 let service
= this._getValue(aHandlerAppID
, NC_SERVICE
);
585 let method
= this._getValue(aHandlerAppID
, NC_METHOD
);
589 let objpath
= this._getValue(aHandlerAppID
, NC_OBJPATH
);
593 let interface = this._getValue(aHandlerAppID
, NC_INTERFACE
);
597 handlerApp
= Cc
["@mozilla.org/uriloader/dbus-handler-app;1"].
598 createInstance(Ci
.nsIDBusHandlerApp
);
599 handlerApp
.service
= service
;
600 handlerApp
.method
= method
;
601 handlerApp
.objectPath
= objpath
;
602 handlerApp
.dBusInterface
= interface;
608 handlerApp
.name
= this._getValue(aHandlerAppID
, NC_PRETTY_NAME
);
614 * Retrieve file extensions, if any, for the MIME type with the given type ID.
616 * @param aTypeID {string} the type record ID
618 _retrieveFileExtensions
: function HS__retrieveFileExtensions(aTypeID
) {
619 var fileExtensions
= [];
621 var fileExtensionTargets
= this._getTargets(aTypeID
, NC_FILE_EXTENSIONS
);
623 while (fileExtensionTargets
.hasMoreElements()) {
624 let fileExtensionTarget
= fileExtensionTargets
.getNext();
625 if (fileExtensionTarget
instanceof Ci
.nsIRDFLiteral
&&
626 fileExtensionTarget
.Value
!= "")
627 fileExtensions
.push(fileExtensionTarget
.Value
);
630 return fileExtensions
;
634 * Get the file with the given path. This is not as simple as merely
635 * initializing a local file object with the path, because the path might be
636 * relative to the current process directory, in which case we have to
637 * construct a path starting from that directory.
639 * @param aPath {string} a path to a file
641 * @returns {nsILocalFile} the file, or null if the file does not exist
643 _getFileWithPath
: function HS__getFileWithPath(aPath
) {
644 var file
= Cc
["@mozilla.org/file/local;1"].createInstance(Ci
.nsILocalFile
);
647 file
.initWithPath(aPath
);
653 // Note: for historical reasons, we don't actually check to see
654 // if the exception is NS_ERROR_FILE_UNRECOGNIZED_PATH, which is what
655 // nsILocalFile::initWithPath throws when a path is relative.
657 file
= this._dirSvc
.get("XCurProcD", Ci
.nsIFile
);
671 //**************************************************************************//
674 _storePreferredAction
: function HS__storePreferredAction(aHandlerInfo
) {
675 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
677 switch(aHandlerInfo
.preferredAction
) {
678 case Ci
.nsIHandlerInfo
.saveToDisk
:
679 this._setLiteral(infoID
, NC_SAVE_TO_DISK
, "true");
680 this._removeTarget(infoID
, NC_HANDLE_INTERNALLY
);
681 this._removeTarget(infoID
, NC_USE_SYSTEM_DEFAULT
);
684 case Ci
.nsIHandlerInfo
.handleInternally
:
685 this._setLiteral(infoID
, NC_HANDLE_INTERNALLY
, "true");
686 this._removeTarget(infoID
, NC_SAVE_TO_DISK
);
687 this._removeTarget(infoID
, NC_USE_SYSTEM_DEFAULT
);
690 case Ci
.nsIHandlerInfo
.useSystemDefault
:
691 this._setLiteral(infoID
, NC_USE_SYSTEM_DEFAULT
, "true");
692 this._removeTarget(infoID
, NC_SAVE_TO_DISK
);
693 this._removeTarget(infoID
, NC_HANDLE_INTERNALLY
);
696 // This value is indicated in the datastore either by the absence of
697 // the three properties or by setting them all "false". Of these two
698 // options, the former seems preferable, because it reduces the size
699 // of the RDF file and thus the amount of stuff we have to parse.
700 case Ci
.nsIHandlerInfo
.useHelperApp
:
702 this._removeTarget(infoID
, NC_SAVE_TO_DISK
);
703 this._removeTarget(infoID
, NC_HANDLE_INTERNALLY
);
704 this._removeTarget(infoID
, NC_USE_SYSTEM_DEFAULT
);
709 _storePreferredHandler
: function HS__storePreferredHandler(aHandlerInfo
) {
710 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
712 this._getPreferredHandlerID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
714 var handler
= aHandlerInfo
.preferredApplicationHandler
;
717 this._storeHandlerApp(handlerID
, handler
);
719 // Make this app be the preferred app for the handler info.
721 // Note: nsExternalHelperAppService::FillContentHandlerProperties ignores
722 // this setting and instead identifies the preferred app as the resource
723 // whose URI follows the pattern urn:<class>:externalApplication:<type>.
724 // But the old downloadactions.js code used to set this property, so just
725 // in case there is still some code somewhere that relies on its presence,
727 this._setResource(infoID
, NC_PREFERRED_APP
, handlerID
);
730 // There isn't a preferred handler. Remove the existing record for it,
732 this._removeTarget(infoID
, NC_PREFERRED_APP
);
733 this._removeAssertions(handlerID
);
738 * Store the list of possible handler apps for the content type represented
739 * by the given handler info object.
741 * @param aHandlerInfo {nsIHandlerInfo} the handler info object
743 _storePossibleHandlers
: function HS__storePossibleHandlers(aHandlerInfo
) {
744 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
746 // First, retrieve the set of handler apps currently stored for the type,
747 // keeping track of their IDs in a hash that we'll use to determine which
748 // ones are no longer valid and should be removed.
749 var currentHandlerApps
= {};
750 var currentHandlerTargets
= this._getTargets(infoID
, NC_POSSIBLE_APP
);
751 while (currentHandlerTargets
.hasMoreElements()) {
752 let handlerApp
= currentHandlerTargets
.getNext();
753 if (handlerApp
instanceof Ci
.nsIRDFResource
) {
754 let handlerAppID
= handlerApp
.ValueUTF8
;
755 currentHandlerApps
[handlerAppID
] = true;
759 // Next, store any new handler apps.
761 aHandlerInfo
.possibleApplicationHandlers
.enumerate();
762 while (newHandlerApps
.hasMoreElements()) {
764 newHandlerApps
.getNext().QueryInterface(Ci
.nsIHandlerApp
);
765 let handlerAppID
= this._getPossibleHandlerAppID(handlerApp
);
766 if (!this._hasResourceAssertion(infoID
, NC_POSSIBLE_APP
, handlerAppID
)) {
767 this._storeHandlerApp(handlerAppID
, handlerApp
);
768 this._addResourceAssertion(infoID
, NC_POSSIBLE_APP
, handlerAppID
);
770 delete currentHandlerApps
[handlerAppID
];
773 // Finally, remove any old handler apps that aren't being used anymore,
774 // and if those handler apps aren't being used by any other type either,
775 // then completely remove their record from the datastore so we don't
776 // leave it clogged up with information about handler apps we don't care
778 for (let handlerAppID
in currentHandlerApps
) {
779 this._removeResourceAssertion(infoID
, NC_POSSIBLE_APP
, handlerAppID
);
780 if (!this._existsResourceTarget(NC_POSSIBLE_APP
, handlerAppID
))
781 this._removeAssertions(handlerAppID
);
786 * Store the given handler app.
788 * Note: the reason this method takes the ID of the handler app in a param
789 * is that the ID is different than it usually is when the handler app
790 * in question is a preferred handler app, so this method can't just derive
791 * the ID of the handler app by calling _getPossibleHandlerAppID, its callers
792 * have to do that for it.
794 * @param aHandlerAppID {string} the ID of the handler app to store
795 * @param aHandlerApp {nsIHandlerApp} the handler app to store
797 _storeHandlerApp
: function HS__storeHandlerApp(aHandlerAppID
, aHandlerApp
) {
798 aHandlerApp
.QueryInterface(Ci
.nsIHandlerApp
);
799 this._setLiteral(aHandlerAppID
, NC_PRETTY_NAME
, aHandlerApp
.name
);
801 // In the case of the preferred handler, the handler ID could have been
802 // used to refer to a different kind of handler in the past (i.e. either
803 // a local hander or a web handler), so if the new handler is a local
804 // handler, then we remove any web handler properties and vice versa.
805 // This is unnecessary but harmless for possible handlers.
807 if (aHandlerApp
instanceof Ci
.nsILocalHandlerApp
) {
808 this._setLiteral(aHandlerAppID
, NC_PATH
, aHandlerApp
.executable
.path
);
809 this._removeTarget(aHandlerAppID
, NC_URI_TEMPLATE
);
810 this._removeTarget(aHandlerAppID
, NC_METHOD
);
811 this._removeTarget(aHandlerAppID
, NC_SERVICE
);
812 this._removeTarget(aHandlerAppID
, NC_OBJPATH
);
813 this._removeTarget(aHandlerAppID
, NC_INTERFACE
);
815 else if(aHandlerApp
instanceof Ci
.nsIWebHandlerApp
){
816 aHandlerApp
.QueryInterface(Ci
.nsIWebHandlerApp
);
817 this._setLiteral(aHandlerAppID
, NC_URI_TEMPLATE
, aHandlerApp
.uriTemplate
);
818 this._removeTarget(aHandlerAppID
, NC_PATH
);
819 this._removeTarget(aHandlerAppID
, NC_METHOD
);
820 this._removeTarget(aHandlerAppID
, NC_SERVICE
);
821 this._removeTarget(aHandlerAppID
, NC_OBJPATH
);
822 this._removeTarget(aHandlerAppID
, NC_INTERFACE
);
824 else if(aHandlerApp
instanceof Ci
.nsIDBusHandlerApp
){
825 aHandlerApp
.QueryInterface(Ci
.nsIDBusHandlerApp
);
826 this._setLiteral(aHandlerAppID
, NC_SERVICE
, aHandlerApp
.service
);
827 this._setLiteral(aHandlerAppID
, NC_METHOD
, aHandlerApp
.method
);
828 this._setLiteral(aHandlerAppID
, NC_OBJPATH
, aHandlerApp
.objectPath
);
829 this._setLiteral(aHandlerAppID
, NC_INTERFACE
, aHandlerApp
.dBusInterface
);
830 this._removeTarget(aHandlerAppID
, NC_PATH
);
831 this._removeTarget(aHandlerAppID
, NC_URI_TEMPLATE
);
834 throw "unknown handler type";
839 _storeAlwaysAsk
: function HS__storeAlwaysAsk(aHandlerInfo
) {
840 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
841 this._setLiteral(infoID
,
843 aHandlerInfo
.alwaysAskBeforeHandling
? "true" : "false");
847 //**************************************************************************//
848 // Convenience Getters
853 if (!this.__observerSvc
)
855 Cc
["@mozilla.org/observer-service;1"].
856 getService(Ci
.nsIObserverService
);
857 return this.__observerSvc
;
865 Cc
["@mozilla.org/file/directory_service;1"].
866 getService(Ci
.nsIProperties
);
867 return this.__dirSvc
;
875 Cc
["@mozilla.org/mime;1"].
876 getService(Ci
.nsIMIMEService
);
877 return this.__mimeSvc
;
883 if (!this.__protocolSvc
)
885 Cc
["@mozilla.org/uriloader/external-protocol-service;1"].
886 getService(Ci
.nsIExternalProtocolService
);
887 return this.__protocolSvc
;
894 this.__rdf
= Cc
["@mozilla.org/rdf/rdf-service;1"].
895 getService(Ci
.nsIRDFService
);
899 // RDF Container Utils
900 __containerUtils
: null,
901 get _containerUtils() {
902 if (!this.__containerUtils
)
903 this.__containerUtils
= Cc
["@mozilla.org/rdf/container-utils;1"].
904 getService(Ci
.nsIRDFContainerUtils
);
905 return this.__containerUtils
;
908 // RDF datasource containing content handling config (i.e. mimeTypes.rdf)
912 var file
= this._dirSvc
.get("UMimTyp", Ci
.nsIFile
);
913 // FIXME: make this a memoizing getter if we use it anywhere else.
914 var ioService
= Cc
["@mozilla.org/network/io-service;1"].
915 getService(Ci
.nsIIOService
);
916 var fileHandler
= ioService
.getProtocolHandler("file").
917 QueryInterface(Ci
.nsIFileProtocolHandler
);
919 this._rdf
.GetDataSourceBlocking(fileHandler
.getURLSpecFromFile(file
));
926 //**************************************************************************//
930 * Get the string identifying whether this is a MIME or a protocol handler.
931 * This string is used in the URI IDs of various RDF properties.
933 * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the class
935 * @returns {string} the class
937 _getClass
: function HS__getClass(aHandlerInfo
) {
938 if (aHandlerInfo
instanceof Ci
.nsIMIMEInfo
)
939 return CLASS_MIMEINFO
;
941 return CLASS_PROTOCOLINFO
;
945 * Return the unique identifier for a content type record, which stores
946 * the value field plus a reference to the content type's handler info record.
948 * |urn:<class>:<type>|
950 * XXX: should this be a property of nsIHandlerInfo?
952 * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
953 * @param aType {string} the type (a MIME type or protocol scheme)
955 * @returns {string} the ID
957 _getTypeID
: function HS__getTypeID(aClass
, aType
) {
958 return "urn:" + aClass
+ ":" + aType
;
962 * Return the unique identifier for a handler info record, which stores
963 * the preferredAction and alwaysAsk fields plus a reference to the preferred
964 * handler app. Roughly equivalent to the nsIHandlerInfo interface.
966 * |urn:<class>:handler:<type>|
968 * FIXME: the type info record should be merged into the type record,
969 * since there's a one to one relationship between them, and this record
970 * merely stores additional attributes of a content type.
972 * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
973 * @param aType {string} the type (a MIME type or protocol scheme)
975 * @returns {string} the ID
977 _getInfoID
: function HS__getInfoID(aClass
, aType
) {
978 return "urn:" + aClass
+ ":handler:" + aType
;
982 * Return the unique identifier for a preferred handler record, which stores
983 * information about the preferred handler for a given content type, including
984 * its human-readable name and the path to its executable (for a local app)
985 * or its URI template (for a web app).
987 * |urn:<class>:externalApplication:<type>|
989 * XXX: should this be a property of nsIHandlerApp?
991 * FIXME: this should be an arbitrary ID, and we should retrieve it from
992 * the datastore for a given content type via the NC:ExternalApplication
993 * property rather than looking for a specific ID, so a handler doesn't
994 * have to change IDs when it goes from being a possible handler to being
995 * the preferred one (once we support possible handlers).
997 * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
998 * @param aType {string} the type (a MIME type or protocol scheme)
1000 * @returns {string} the ID
1002 _getPreferredHandlerID
: function HS__getPreferredHandlerID(aClass
, aType
) {
1003 return "urn:" + aClass
+ ":externalApplication:" + aType
;
1007 * Return the unique identifier for a handler app record, which stores
1008 * information about a possible handler for one or more content types,
1009 * including its human-readable name and the path to its executable (for a
1010 * local app) or its URI template (for a web app).
1012 * Note: handler app IDs for preferred handlers are different. For those,
1013 * see the _getPreferredHandlerID method.
1015 * @param aHandlerApp {nsIHandlerApp} the handler app object
1017 _getPossibleHandlerAppID
: function HS__getPossibleHandlerAppID(aHandlerApp
) {
1018 var handlerAppID
= "urn:handler:";
1020 if (aHandlerApp
instanceof Ci
.nsILocalHandlerApp
)
1021 handlerAppID
+= "local:" + aHandlerApp
.executable
.path
;
1022 else if(aHandlerApp
instanceof Ci
.nsIWebHandlerApp
){
1023 aHandlerApp
.QueryInterface(Ci
.nsIWebHandlerApp
);
1024 handlerAppID
+= "web:" + aHandlerApp
.uriTemplate
;
1026 else if(aHandlerApp
instanceof Ci
.nsIDBusHandlerApp
){
1027 aHandlerApp
.QueryInterface(Ci
.nsIDBusHandlerApp
);
1028 handlerAppID
+= "dbus:" + aHandlerApp
.service
+ " " + aHandlerApp
.method
+ " " + aHandlerApp
.uriTemplate
;
1030 throw "unknown handler type";
1033 return handlerAppID
;
1037 * Get the list of types for the given class, creating the list if it doesn't
1038 * already exist. The class can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO
1039 * (i.e. the result of a call to _getClass).
1042 * |urn:<class>s:root|
1044 * @param aClass {string} the class for which to retrieve a list of types
1046 * @returns {nsIRDFContainer} the list of types
1048 _ensureAndGetTypeList
: function HS__ensureAndGetTypeList(aClass
) {
1049 var source
= this._rdf
.GetResource("urn:" + aClass
+ "s");
1051 this._rdf
.GetResource(aClass
== CLASS_MIMEINFO
? NC_MIME_TYPES
1052 : NC_PROTOCOL_SCHEMES
);
1053 var target
= this._rdf
.GetResource("urn:" + aClass
+ "s:root");
1055 // Make sure we have an arc from the source to the target.
1056 if (!this._ds
.HasAssertion(source
, property
, target
, true))
1057 this._ds
.Assert(source
, property
, target
, true);
1059 // Make sure the target is a container.
1060 if (!this._containerUtils
.IsContainer(this._ds
, target
))
1061 this._containerUtils
.MakeSeq(this._ds
, target
);
1063 // Get the type list as an RDF container.
1064 var typeList
= Cc
["@mozilla.org/rdf/container;1"].
1065 createInstance(Ci
.nsIRDFContainer
);
1066 typeList
.Init(this._ds
, target
);
1072 * Make sure there are records in the datasource for the given content type
1073 * by creating them if they don't already exist. We have to do this before
1074 * storing any specific data, because we can't assume the presence
1075 * of the records (the nsIHandlerInfo object might have been created
1076 * from the OS), and the records have to all be there in order for the helper
1077 * app service to properly construct an nsIHandlerInfo object for the type.
1079 * Based on old downloadactions.js::_ensureMIMERegistryEntry.
1081 * @param aHandlerInfo {nsIHandlerInfo} the type to make sure has a record
1083 _ensureRecordsForType
: function HS__ensureRecordsForType(aHandlerInfo
) {
1084 // Get the list of types.
1085 var typeList
= this._ensureAndGetTypeList(this._getClass(aHandlerInfo
));
1087 // If there's already a record in the datastore for this type, then we
1088 // don't need to do anything more.
1089 var typeID
= this._getTypeID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
1090 var type
= this._rdf
.GetResource(typeID
);
1091 if (typeList
.IndexOf(type
) != -1)
1094 // Create a basic type record for this type.
1095 typeList
.AppendElement(type
);
1096 this._setLiteral(typeID
, NC_VALUE
, aHandlerInfo
.type
);
1098 // Create a basic info record for this type.
1099 var infoID
= this._getInfoID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
1100 this._setLiteral(infoID
, NC_ALWAYS_ASK
, "false");
1101 this._setResource(typeID
, NC_HANDLER_INFO
, infoID
);
1102 // XXX Shouldn't we set preferredAction to useSystemDefault?
1103 // That's what it is if there's no record in the datastore; why should it
1104 // change to useHelperApp just because we add a record to the datastore?
1106 // Create a basic preferred handler record for this type.
1107 // XXX Not sure this is necessary, since preferred handlers are optional,
1108 // and nsExternalHelperAppService::FillHandlerInfoForTypeFromDS doesn't seem
1109 // to require the record , but downloadactions.js::_ensureMIMERegistryEntry
1110 // used to create it, so we'll do the same.
1111 var preferredHandlerID
=
1112 this._getPreferredHandlerID(this._getClass(aHandlerInfo
), aHandlerInfo
.type
);
1113 this._setLiteral(preferredHandlerID
, NC_PATH
, "");
1114 this._setResource(infoID
, NC_PREFERRED_APP
, preferredHandlerID
);
1118 * Append known handlers of the given class to the given array. The class
1119 * can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO.
1121 * @param aHandlers {array} the array of handlers to append to
1122 * @param aClass {string} the class for which to append handlers
1124 _appendHandlers
: function HS__appendHandlers(aHandlers
, aClass
) {
1125 var typeList
= this._ensureAndGetTypeList(aClass
);
1126 var enumerator
= typeList
.GetElements();
1128 while (enumerator
.hasMoreElements()) {
1129 var element
= enumerator
.getNext();
1131 // This should never happen. If it does, that means our datasource
1132 // is corrupted with type list entries that point to literal values
1133 // instead of resources. If it does happen, let's just do our best
1134 // to recover by ignoring this entry and moving on to the next one.
1135 if (!(element
instanceof Ci
.nsIRDFResource
))
1138 // Get the value of the element's NC:value property, which contains
1139 // the MIME type or scheme for which we're retrieving a handler info.
1140 var type
= this._getValue(element
.ValueUTF8
, NC_VALUE
);
1145 if (typeList
.Resource
.ValueUTF8
== "urn:mimetypes:root")
1146 handler
= this._mimeSvc
.getFromTypeAndExtension(type
, null);
1148 handler
= this._protocolSvc
.getProtocolHandlerInfo(type
);
1150 aHandlers
.appendElement(handler
, false);
1155 * Whether or not a property of an RDF source has a value.
1157 * @param sourceURI {string} the URI of the source
1158 * @param propertyURI {string} the URI of the property
1159 * @returns {boolean} whether or not the property has a value
1161 _hasValue
: function HS__hasValue(sourceURI
, propertyURI
) {
1162 var source
= this._rdf
.GetResource(sourceURI
);
1163 var property
= this._rdf
.GetResource(propertyURI
);
1164 return this._ds
.hasArcOut(source
, property
);
1168 * Get the value of a property of an RDF source.
1170 * @param sourceURI {string} the URI of the source
1171 * @param propertyURI {string} the URI of the property
1172 * @returns {string} the value of the property
1174 _getValue
: function HS__getValue(sourceURI
, propertyURI
) {
1175 var source
= this._rdf
.GetResource(sourceURI
);
1176 var property
= this._rdf
.GetResource(propertyURI
);
1178 var target
= this._ds
.GetTarget(source
, property
, true);
1183 if (target
instanceof Ci
.nsIRDFResource
)
1184 return target
.ValueUTF8
;
1186 if (target
instanceof Ci
.nsIRDFLiteral
)
1187 return target
.Value
;
1193 * Get all targets for the property of an RDF source.
1195 * @param sourceURI {string} the URI of the source
1196 * @param propertyURI {string} the URI of the property
1198 * @returns {nsISimpleEnumerator} an enumerator of targets
1200 _getTargets
: function HS__getTargets(sourceURI
, propertyURI
) {
1201 var source
= this._rdf
.GetResource(sourceURI
);
1202 var property
= this._rdf
.GetResource(propertyURI
);
1204 return this._ds
.GetTargets(source
, property
, true);
1208 * Set a property of an RDF source to a literal value.
1210 * @param sourceURI {string} the URI of the source
1211 * @param propertyURI {string} the URI of the property
1212 * @param value {string} the literal value
1214 _setLiteral
: function HS__setLiteral(sourceURI
, propertyURI
, value
) {
1215 var source
= this._rdf
.GetResource(sourceURI
);
1216 var property
= this._rdf
.GetResource(propertyURI
);
1217 var target
= this._rdf
.GetLiteral(value
);
1219 this._setTarget(source
, property
, target
);
1223 * Set a property of an RDF source to a resource target.
1225 * @param sourceURI {string} the URI of the source
1226 * @param propertyURI {string} the URI of the property
1227 * @param targetURI {string} the URI of the target
1229 _setResource
: function HS__setResource(sourceURI
, propertyURI
, targetURI
) {
1230 var source
= this._rdf
.GetResource(sourceURI
);
1231 var property
= this._rdf
.GetResource(propertyURI
);
1232 var target
= this._rdf
.GetResource(targetURI
);
1234 this._setTarget(source
, property
, target
);
1238 * Assert an arc into the RDF datasource if there is no arc with the given
1239 * source and property; otherwise, if there is already an existing arc,
1240 * change it to point to the given target. _setLiteral and _setResource
1241 * call this after converting their string arguments into resources
1242 * and literals, and most callers should call one of those two methods
1243 * instead of this one.
1245 * @param source {nsIRDFResource} the source
1246 * @param property {nsIRDFResource} the property
1247 * @param target {nsIRDFNode} the target
1249 _setTarget
: function HS__setTarget(source
, property
, target
) {
1250 if (this._ds
.hasArcOut(source
, property
)) {
1251 var oldTarget
= this._ds
.GetTarget(source
, property
, true);
1252 this._ds
.Change(source
, property
, oldTarget
, target
);
1255 this._ds
.Assert(source
, property
, target
, true);
1259 * Assert that a property of an RDF source has a resource target.
1261 * The difference between this method and _setResource is that this one adds
1262 * an assertion even if one already exists, which allows its callers to make
1263 * sets of assertions (i.e. to set a property to multiple targets).
1265 * @param sourceURI {string} the URI of the source
1266 * @param propertyURI {string} the URI of the property
1267 * @param targetURI {string} the URI of the target
1269 _addResourceAssertion
: function HS__addResourceAssertion(sourceURI
,
1272 var source
= this._rdf
.GetResource(sourceURI
);
1273 var property
= this._rdf
.GetResource(propertyURI
);
1274 var target
= this._rdf
.GetResource(targetURI
);
1276 this._ds
.Assert(source
, property
, target
, true);
1280 * Remove an assertion with a resource target.
1282 * @param sourceURI {string} the URI of the source
1283 * @param propertyURI {string} the URI of the property
1284 * @param targetURI {string} the URI of the target
1286 _removeResourceAssertion
: function HS__removeResourceAssertion(sourceURI
,
1289 var source
= this._rdf
.GetResource(sourceURI
);
1290 var property
= this._rdf
.GetResource(propertyURI
);
1291 var target
= this._rdf
.GetResource(targetURI
);
1293 this._ds
.Unassert(source
, property
, target
);
1297 * Whether or not a property of an RDF source has a given resource target.
1299 * @param sourceURI {string} the URI of the source
1300 * @param propertyURI {string} the URI of the property
1301 * @param targetURI {string} the URI of the target
1303 * @returns {boolean} whether or not there is such an assertion
1305 _hasResourceAssertion
: function HS__hasResourceAssertion(sourceURI
,
1308 var source
= this._rdf
.GetResource(sourceURI
);
1309 var property
= this._rdf
.GetResource(propertyURI
);
1310 var target
= this._rdf
.GetResource(targetURI
);
1312 return this._ds
.HasAssertion(source
, property
, target
, true);
1316 * Whether or not a property of an RDF source has a given literal value.
1318 * @param sourceURI {string} the URI of the source
1319 * @param propertyURI {string} the URI of the property
1320 * @param value {string} the literal value
1322 * @returns {boolean} whether or not there is such an assertion
1324 _hasLiteralAssertion
: function HS__hasLiteralAssertion(sourceURI
,
1327 var source
= this._rdf
.GetResource(sourceURI
);
1328 var property
= this._rdf
.GetResource(propertyURI
);
1329 var target
= this._rdf
.GetLiteral(value
);
1331 return this._ds
.HasAssertion(source
, property
, target
, true);
1335 * Whether or not there is an RDF source that has the given property set to
1336 * the given literal value.
1338 * @param propertyURI {string} the URI of the property
1339 * @param value {string} the literal value
1341 * @returns {boolean} whether or not there is a source
1343 _existsLiteralTarget
: function HS__existsLiteralTarget(propertyURI
, value
) {
1344 var property
= this._rdf
.GetResource(propertyURI
);
1345 var target
= this._rdf
.GetLiteral(value
);
1347 return this._ds
.hasArcIn(target
, property
);
1351 * Get the source for a property set to a given literal value.
1353 * @param propertyURI {string} the URI of the property
1354 * @param value {string} the literal value
1356 _getSourceForLiteral
: function HS__getSourceForLiteral(propertyURI
, value
) {
1357 var property
= this._rdf
.GetResource(propertyURI
);
1358 var target
= this._rdf
.GetLiteral(value
);
1360 var source
= this._ds
.GetSource(property
, target
, true);
1362 return source
.ValueUTF8
;
1368 * Whether or not there is an RDF source that has the given property set to
1369 * the given resource target.
1371 * @param propertyURI {string} the URI of the property
1372 * @param targetURI {string} the URI of the target
1374 * @returns {boolean} whether or not there is a source
1376 _existsResourceTarget
: function HS__existsResourceTarget(propertyURI
,
1378 var property
= this._rdf
.GetResource(propertyURI
);
1379 var target
= this._rdf
.GetResource(targetURI
);
1381 return this._ds
.hasArcIn(target
, property
);
1385 * Remove a property of an RDF source.
1387 * @param sourceURI {string} the URI of the source
1388 * @param propertyURI {string} the URI of the property
1390 _removeTarget
: function HS__removeTarget(sourceURI
, propertyURI
) {
1391 var source
= this._rdf
.GetResource(sourceURI
);
1392 var property
= this._rdf
.GetResource(propertyURI
);
1394 if (this._ds
.hasArcOut(source
, property
)) {
1395 var target
= this._ds
.GetTarget(source
, property
, true);
1396 this._ds
.Unassert(source
, property
, target
);
1401 * Remove all assertions about a given RDF source.
1403 * Note: not recursive. If some assertions point to other resources,
1404 * and you want to remove assertions about those resources too, you need
1405 * to do so manually.
1407 * @param sourceURI {string} the URI of the source
1409 _removeAssertions
: function HS__removeAssertions(sourceURI
) {
1410 var source
= this._rdf
.GetResource(sourceURI
);
1411 var properties
= this._ds
.ArcLabelsOut(source
);
1413 while (properties
.hasMoreElements()) {
1414 let property
= properties
.getNext();
1415 let targets
= this._ds
.GetTargets(source
, property
, true);
1416 while (targets
.hasMoreElements()) {
1417 let target
= targets
.getNext();
1418 this._ds
.Unassert(source
, property
, target
);
1426 //****************************************************************************//
1427 // More XPCOM Plumbing
1429 function NSGetModule(compMgr
, fileSpec
) {
1430 return XPCOMUtils
.generateModule([HandlerService
]);