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