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 Bookmarks Sync.
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
* Dan Mills <thunder@mozilla.com>
23
* Alternatively, the contents of this file may be used under the terms of
24
* either the GNU General Public License Version 2 or later (the "GPL"), or
25
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
* in which case the provisions of the GPL or the LGPL are applicable instead
27
* of those above. If you wish to allow use of your version of this file only
28
* under the terms of either the GPL or the LGPL, and not to allow others to
29
* use your version of this file under the terms of the MPL, indicate your
30
* decision by deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL or the LGPL. If you do not delete
32
* the provisions above, a recipient may use your version of this file under
33
* the terms of any one of the MPL, the GPL or the LGPL.
35
* ***** END LICENSE BLOCK ***** */
37
const EXPORTED_SYMBOLS = ['Utils', 'Svc', 'Str'];
39
const Cc = Components.classes;
40
const Ci = Components.interfaces;
41
const Cr = Components.results;
42
const Cu = Components.utils;
44
Cu.import("resource://weave/ext/Preferences.js");
45
Cu.import("resource://weave/ext/Observers.js");
46
Cu.import("resource://weave/ext/StringBundle.js");
47
Cu.import("resource://weave/constants.js");
48
Cu.import("resource://weave/log4moz.js");
56
* Wrap a function to catch all exceptions and log them
58
* @usage MyObj._catch = Utils.catch;
59
* MyObj.foo = function() { this._catch(func)(); }
61
catch: function Utils_catch(func) {
63
return function WrappedCatch() {
65
return func.call(thisArg);
68
thisArg._log.debug("Exception: " + Utils.exceptionStr(ex));
74
* Wrap a function to call lock before calling the function then unlock.
76
* @usage MyObj._lock = Utils.lock;
77
* MyObj.foo = function() { this._lock(func)(); }
79
lock: function Utils_lock(func) {
81
return function WrappedLock() {
83
throw "Could not acquire lock";
86
return func.call(thisArg);
95
* Wrap functions to notify when it starts and finishes executing or if it got
96
* an error. The message is a combination of a provided prefix and local name
97
* with the current state and the subject is the provided subject.
99
* @usage function MyObj() { this._notify = Utils.notify("prefix:"); }
100
* MyObj.foo = function() { this._notify(name, subject, func)(); }
102
notify: function Utils_notify(prefix) {
103
return function NotifyMaker(name, subject, func) {
105
let notify = function(state) {
106
let mesg = prefix + name + ":" + state;
107
thisArg._log.trace("Event: " + mesg);
108
Observers.notify(mesg, subject);
111
return function WrappedNotify() {
114
let ret = func.call(thisArg);
126
batchSync: function batchSync(service, engineType) {
127
return function batchedSync() {
131
// Try running sync in batch mode
132
Svc[service].runInBatchMode({
133
runBatched: function wrappedSync() {
135
engineType.prototype._sync.call(engine);
143
// Expose the exception if something inside the batch failed
149
// Generates a brand-new globally unique identifier (GUID).
150
makeGUID: function makeGUID() {
151
// 70 characters that are not-escaped URL-friendly
153
"!()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
159
// Generate ten 70-value characters for a 70^10 (~61.29-bit) GUID
160
for (let i = 0; i < 10; i++) {
161
// Refresh the number source after using it a few times
162
if (i == 0 || i == 5)
165
// Figure out which code to use for the next GUID character
167
val = Math.floor(num);
175
anno: function anno(id, anno, val, expire) {
176
// Figure out if we have a bookmark or page
177
let annoFunc = (typeof id == "number" ? "Item" : "Page") + "Annotation";
179
// Convert to a nsIURI if necessary
180
if (typeof id == "string")
181
id = Utils.makeURI(id);
184
throw "Null id for anno! (invalid uri)";
186
switch (arguments.length) {
188
// Get the annotation with 2 args
189
return Svc.Annos["get" + annoFunc](id, anno);
194
// Convert to actual EXPIRE value
195
expire = Svc.Annos["EXPIRE_" + expire];
197
// Set the annotation with 3 or 4 args
198
return Svc.Annos["set" + annoFunc](id, anno, val, 0, expire);
202
ensureOneOpen: let (windows = {}) function ensureOneOpen(window) {
203
// Close the other window if it exists
204
let url = window.location.href;
205
let other = windows[url];
209
// Save the new window for future closure
210
windows[url] = window;
212
// Actively clean up when the window is closed
213
window.addEventListener("unload", function() windows[url] = null, false);
216
// Returns a nsILocalFile representing a file relative to the
217
// current user's profile directory. If the argument is a string,
218
// it should be a string with unix-style slashes for directory names
219
// (these slashes are automatically converted to platform-specific
222
// Alternatively, if the argument is an object, it should contain
223
// the following attributes:
225
// path: the path to the file, relative to the current user's
228
// autoCreate: whether or not the file should be created if it
229
// doesn't already exist.
230
getProfileFile: function getProfileFile(arg) {
231
if (typeof arg == "string")
234
let pathParts = arg.path.split("/");
235
let file = Svc.Directory.get("ProfD", Ci.nsIFile);
236
file.QueryInterface(Ci.nsILocalFile);
237
for (let i = 0; i < pathParts.length; i++)
238
file.append(pathParts[i]);
239
if (arg.autoCreate && !file.exists())
240
file.create(file.NORMAL_FILE_TYPE, PERMS_FILE);
245
* Add a simple getter/setter to an object that defers access of a property
246
* to an inner property.
249
* Object to add properties to defer in its prototype
251
* Hash property of obj to defer to (dot split each level)
253
* Property name to defer (or an array of property names)
255
deferGetSet: function Utils_deferGetSet(obj, defer, prop) {
256
if (Utils.isArray(prop))
257
return prop.map(function(prop) Utils.deferGetSet(obj, defer, prop));
259
// Split the defer into each dot part for each level to dereference
260
let parts = defer.split(".");
261
let deref = function(base) Utils.deref(base, parts);
263
let prot = obj.prototype;
265
// Create a getter if it doesn't exist yet
266
if (!prot.__lookupGetter__(prop))
267
prot.__defineGetter__(prop, function() deref(this)[prop]);
269
// Create a setter if it doesn't exist yet
270
if (!prot.__lookupSetter__(prop))
271
prot.__defineSetter__(prop, function(val) deref(this)[prop] = val);
275
* Dereference an array of properties starting from a base object
278
* Base object to start dereferencing
280
* Array of properties to dereference (one for each level)
282
deref: function Utils_deref(base, props) props.reduce(function(curr, prop)
286
* Determine if some value is an array
289
* Value to check (can be null, undefined, etc.)
290
* @return True if it's an array; false otherwise
292
isArray: function Utils_isArray(val) val != null && typeof val == "object" &&
293
val.constructor.name == "Array",
295
// lazy load objects from a constructor on first access. It will
296
// work with the global object ('this' in the global context).
297
lazy: function Weave_lazy(dest, prop, ctr) {
299
dest.__defineGetter__(prop, Utils.lazyCb(dest, prop, ctr));
301
lazyCb: function Weave_lazyCb(dest, prop, ctr) {
304
dest[prop] = new ctr();
309
// like lazy, but rather than new'ing the 3rd arg we use its return value
310
lazy2: function Weave_lazy2(dest, prop, fn) {
312
dest.__defineGetter__(prop, Utils.lazyCb2(dest, prop, fn));
314
lazyCb2: function Weave_lazyCb2(dest, prop, fn) {
317
return dest[prop] = fn();
321
lazySvc: function Weave_lazySvc(dest, prop, cid, iface) {
322
let getter = function() {
326
// Use the platform's service if it exists
327
if (cid in Cc && iface in Ci)
328
svc = Cc[cid].getService(Ci[iface]);
332
let log = Log4Moz.repository.getLogger("Service.Util");
334
log.warn("Component " + cid + " doesn't exist on this platform.");
336
log.debug("Using a fake svc object for " + cid);
339
return dest[prop] = svc;
341
dest.__defineGetter__(prop, getter);
344
lazyStrings: function Weave_lazyStrings(name) {
345
let bundle = "chrome://weave/locale/" + name + ".properties";
346
return function() new StringBundle(bundle);
349
deepEquals: function eq(a, b) {
350
// If they're triple equals, then it must be equals!
354
// If they weren't equal, they must be objects to be different
355
if (typeof a != "object" || typeof b != "object")
358
// But null objects won't have properties to compare
359
if (a === null || b === null)
362
// Make sure all of a's keys have a matching value in b
367
// Do the same for b's keys but skip those that we already checked
369
if (!(k in a) && !eq(a[k], b[k]))
375
deepCopy: function Weave_deepCopy(thing, noSort) {
376
if (typeof(thing) != "object" || thing == null)
380
if (Utils.isArray(thing)) {
382
for (let i = 0; i < thing.length; i++)
383
ret.push(Utils.deepCopy(thing[i], noSort));
387
let props = [p for (p in thing)];
389
props = props.sort();
390
props.forEach(function(k) ret[k] = Utils.deepCopy(thing[k], noSort));
396
// Works on frames or exceptions, munges file:// URIs to shorten the paths
397
// FIXME: filename munging is sort of hackish, might be confusing if
398
// there are multiple extensions with similar filenames
399
formatFrame: function Utils_formatFrame(frame) {
400
let tmp = "<file:unknown>";
402
let file = frame.filename || frame.fileName;
404
tmp = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1");
406
if (frame.lineNumber)
407
tmp += ":" + frame.lineNumber;
409
tmp = frame.name + "()@" + tmp;
414
exceptionStr: function Weave_exceptionStr(e) {
415
let message = e.message ? e.message : e;
416
return message + " " + Utils.stackTrace(e);
419
stackTraceFromFrame: function Weave_stackTraceFromFrame(frame) {
422
let str = Utils.formatFrame(frame);
425
frame = frame.caller;
427
return output.join(" < ");
430
stackTrace: function Weave_stackTrace(e) {
431
// Wrapped nsIException
433
return "Stack trace: " + Utils.stackTraceFromFrame(e.location);
435
// Standard JS exception
437
return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < ").
438
replace(/@(?:chrome|file):.*?([^\/\.]+\.\w+:)/g, "@$1");
440
return "No traceback available";
443
checkStatus: function Weave_checkStatus(code, msg, ranges) {
445
ranges = [[200,300]];
447
for (let i = 0; i < ranges.length; i++) {
449
if (typeof(rng) == "object" && code >= rng[0] && code < rng[1])
451
else if (typeof(rng) == "number" && code == rng) {
457
let log = Log4Moz.repository.getLogger("Service.Util");
458
log.error(msg + " Error code: " + code);
464
ensureStatus: function Weave_ensureStatus(args) {
465
if (!Utils.checkStatus.apply(Utils, arguments))
466
throw 'checkStatus failed';
469
digest: function digest(message, hasher) {
470
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
471
createInstance(Ci.nsIScriptableUnicodeConverter);
472
converter.charset = "UTF-8";
474
let data = converter.convertToByteArray(message, {});
475
hasher.update(data, data.length);
477
// Convert each hashed byte into 2-hex strings then combine them
478
return [("0" + byte.charCodeAt().toString(16)).slice(-2) for each (byte in
479
hasher.finish(false))].join("");
482
sha1: function sha1(message) {
483
let hasher = Cc["@mozilla.org/security/hash;1"].
484
createInstance(Ci.nsICryptoHash);
485
hasher.init(hasher.SHA1);
486
return Utils.digest(message, hasher);
490
* Generate a sha256 HMAC for a string message and a given nsIKeyObject
492
sha256HMAC: function sha256HMAC(message, key) {
493
let hasher = Cc["@mozilla.org/security/hmac;1"].
494
createInstance(Ci.nsICryptoHMAC);
495
hasher.init(hasher.SHA256, key);
496
return Utils.digest(message, hasher);
499
makeURI: function Weave_makeURI(URIString) {
503
return Svc.IO.newURI(URIString, null, null);
505
let log = Log4Moz.repository.getLogger("Service.Util");
506
log.debug("Could not create URI: " + Utils.exceptionStr(e));
511
makeURL: function Weave_makeURL(URIString) {
512
let url = Utils.makeURI(URIString);
513
url.QueryInterface(Ci.nsIURL);
517
xpath: function Weave_xpath(xmlDoc, xpathString) {
518
let root = xmlDoc.ownerDocument == null ?
519
xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement;
520
let nsResolver = xmlDoc.createNSResolver(root);
522
return xmlDoc.evaluate(xpathString, xmlDoc, nsResolver,
523
Ci.nsIDOMXPathResult.ANY_TYPE, null);
526
getTmp: function Weave_getTmp(name) {
527
let tmp = Svc.Directory.get("ProfD", Ci.nsIFile);
528
tmp.QueryInterface(Ci.nsILocalFile);
533
tmp.create(tmp.DIRECTORY_TYPE, PERMS_DIRECTORY);
542
* Load a json object from disk
545
* Json file path load from weave/[filePath].json
547
* Object to use for logging and "this" for callback
549
* Function to process json object as its first parameter
551
jsonLoad: function Utils_jsonLoad(filePath, that, callback) {
552
filePath = "weave/" + filePath + ".json";
554
that._log.trace("Loading json from disk: " + filePath);
556
let file = Utils.getProfileFile(filePath);
561
let [is] = Utils.open(file, "<");
562
let json = Utils.readStream(is);
564
callback.call(that, JSON.parse(json));
568
that._log.debug("Failed to load json: " + Utils.exceptionStr(ex));
573
* Save a json-able object to disk
576
* Json file path save to weave/[filePath].json
578
* Object to use for logging and "this" for callback
580
* Function to provide json-able object to save. If this isn't a
581
* function, it'll be used as the object to make a json string.
583
jsonSave: function Utils_jsonSave(filePath, that, callback) {
584
filePath = "weave/" + filePath + ".json";
586
that._log.trace("Saving json to disk: " + filePath);
588
let file = Utils.getProfileFile({ autoCreate: true, path: filePath });
589
let json = typeof callback == "function" ? callback.call(that) : callback;
590
let out = JSON.stringify(json);
591
let [fos] = Utils.open(file, ">");
592
fos.writeString(out);
597
* Return a timer that is scheduled to call the callback after waiting the
598
* provided time or as soon as possible. The timer will be set as a property
599
* of the provided object with the given timer name.
601
delay: function delay(callback, wait, thisObj, name) {
602
// Default to running right away
605
// Use a dummy object if one wasn't provided
606
thisObj = thisObj || {};
608
// Delay an existing timer if it exists
609
if (name in thisObj && thisObj[name] instanceof Ci.nsITimer) {
610
thisObj[name].delay = wait;
614
// Create a special timer that we can add extra properties
616
timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
618
// Provide an easy way to clear out the timer
619
timer.clear = function() {
620
thisObj[name] = null;
624
// Initialize the timer with a smart callback
625
timer.initWithCallback({
626
notify: function notify() {
627
// Clear out the timer once it's been triggered
629
callback.call(thisObj, timer);
631
}, wait, timer.TYPE_ONE_SHOT);
633
return thisObj[name] = timer;
636
open: function open(pathOrFile, mode, perms) {
639
if (pathOrFile instanceof Ci.nsIFile) {
642
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
643
dump("PATH IS" + pathOrFile + "\n");
644
file.initWithPath(pathOrFile);
653
throw "Cannot open file for reading, file does not exist";
654
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
655
createInstance(Ci.nsIFileInputStream);
656
fis.init(file, MODE_RDONLY, perms, 0);
657
stream = Cc["@mozilla.org/intl/converter-input-stream;1"].
658
createInstance(Ci.nsIConverterInputStream);
659
stream.init(fis, "UTF-8", 4096,
660
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
664
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
665
createInstance(Ci.nsIFileOutputStream);
666
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, perms, 0);
667
stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
668
.createInstance(Ci.nsIConverterOutputStream);
669
stream.init(fos, "UTF-8", 4096, 0x0000);
673
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
674
createInstance(Ci.nsIFileOutputStream);
675
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_APPEND, perms, 0);
676
stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
677
.createInstance(Ci.nsIConverterOutputStream);
678
stream.init(fos, "UTF-8", 4096, 0x0000);
682
throw "Illegal mode to open(): " + mode;
685
return [stream, file];
689
* Open/reshow a window/dialog based on its name and type.
692
* Name of the window/dialog to reshow if already open
694
* Opening behavior: "Window" or "Dialog"
696
* More arguments go here depending on the type
698
_openWin: function Utils__openWin(name, type /*, args... */) {
699
// Just re-show the window if it's already open
700
let openedWindow = Svc.WinMediator.getMostRecentWindow("Weave:" + name);
702
openedWindow.focus();
706
// Open up the window/dialog!
707
let win = Svc.WinWatcher;
708
if (type == "Dialog")
709
win = win.activeWindow;
710
win["open" + type].apply(win, Array.slice(arguments, 2));
713
_openChromeWindow: function Utils_openCWindow(name, uri, options, args) {
714
Utils.openWindow(name, "chrome://weave/content/" + uri, options, args);
717
openWindow: function Utils_openWindow(name, uri, options, args) {
718
Utils._openWin(name, "Window", null, uri, "",
719
options || "centerscreen,chrome,dialog,resizable=yes", args);
722
openDialog: function Utils_openDialog(name, uri, options, args) {
723
Utils._openWin(name, "Dialog", "chrome://weave/content/" + uri, "",
724
options || "centerscreen,chrome,dialog,modal,resizable=no", args);
727
openGenericDialog: function Utils_openGenericDialog(type) {
728
this._genericDialogType = type;
729
this.openDialog("ChangeSomething", "generic-change.xul");
732
getIcon: function(iconUri, defaultIcon) {
734
let iconURI = Utils.makeURI(iconUri);
735
return Svc.Favicon.getFaviconLinkForIcon(iconURI).spec;
739
// Just give the provided default icon or the system's default
740
return defaultIcon || Svc.Favicon.defaultFavicon.spec;
743
getErrorString: function Utils_getErrorString(error, args) {
745
return Str.errors.get(error, args || null);
748
// basically returns "Unknown Error"
749
return Str.errors.get("error.reason.unknown");
752
// assumes an nsIConverterInputStream
753
readStream: function Weave_readStream(is) {
754
let ret = "", str = {};
755
while (is.readString(4096, str) != 0) {
762
* Create an array like the first but without elements of the second
764
arraySub: function arraySub(minuend, subtrahend) {
765
return minuend.filter(function(i) subtrahend.indexOf(i) == -1);
768
bind2: function Async_bind2(object, method) {
769
return function innerBind() { return method.apply(object, arguments); };
772
mpLocked: function mpLocked() {
773
let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"].
774
getService(Ci.nsIPKCS11ModuleDB);
775
let sdrSlot = modules.findSlotByName("");
776
let status = sdrSlot.status;
777
let slots = Ci.nsIPKCS11Slot;
779
if (status == slots.SLOT_READY || status == slots.SLOT_LOGGED_IN)
782
if (status == slots.SLOT_NOT_LOGGED_IN)
785
// something wacky happened, pretend MP is locked
792
this.__prefs = Cc["@mozilla.org/preferences-service;1"]
793
.getService(Ci.nsIPrefService);
794
this.__prefs = this.__prefs.getBranch(PREFS_BRANCH);
795
this.__prefs.QueryInterface(Ci.nsIPrefBranch2);
803
"@mozilla.org/privatebrowsing;1": {
805
privateBrowsingEnabled: false
808
"@mozilla.org/browser/sessionstore;1": {
809
setTabValue: function(tab, key, value) {
810
if (!tab.__SS_extdata)
811
tab.__SS_extdata = {};
812
tab.__SS_extData[key] = value;
814
getBrowserState: function() {
815
// Fennec should have only one window. Not more, not less.
816
let state = { windows: [{ tabs: [] }] };
817
let window = Svc.WinMediator.getMostRecentWindow("navigator:browser");
819
// Extract various pieces of tab data
820
window.Browser._tabs.forEach(function(tab) {
821
let tabState = { entries: [{}] };
822
let browser = tab.browser;
824
// Cases when we want to skip the tab. Could come up if we get
825
// state as a tab is opening or closing.
826
if (!browser || !browser.currentURI || !browser.sessionHistory)
829
let history = browser.sessionHistory;
830
if (history.count > 0) {
831
// We're only grabbing the current history entry for now.
832
let entry = history.getEntryAtIndex(history.index, false);
833
tabState.entries[0].url = entry.URI.spec;
834
// Like SessionStore really does it...
835
if (entry.title && entry.title != entry.url)
836
tabState.entries[0].title = entry.title;
841
// Get the image for the tab. Fennec doesn't quite work the same
842
// way as Firefox, so we'll just get this from the browser object.
843
tabState.attributes = { image: browser.mIconURL };
845
// Collect the extdata
846
if (tab.__SS_extdata) {
847
tabState.extData = {};
848
for (let key in tab.__SS_extdata)
849
tabState.extData[key] = tab.__SS_extdata[key];
852
// Add the tab to the window
853
state.windows[0].tabs.push(tabState);
855
return JSON.stringify(state);
858
// A fake service only used for testing
859
"@labs.mozilla.com/Fake/Thing;1": {
864
// Use the binary WeaveCrypto (;1) if the js-ctypes version (;2) fails to load
865
// by adding an alias on FakeSvc from ;2 to ;1
866
Utils.lazySvc(FakeSvc, "@labs.mozilla.com/Weave/Crypto;2",
867
"@labs.mozilla.com/Weave/Crypto;1", "IWeaveCrypto");
870
* Commonly-used services
873
Svc.Prefs = new Preferences(PREFS_BRANCH);
875
[["Annos", "@mozilla.org/browser/annotation-service;1", "nsIAnnotationService"],
876
["AppInfo", "@mozilla.org/xre/app-info;1", "nsIXULAppInfo"],
877
["Bookmark", "@mozilla.org/browser/nav-bookmarks-service;1", "nsINavBookmarksService"],
878
["Crypto", "@labs.mozilla.com/Weave/Crypto;2", "IWeaveCrypto"],
879
["Directory", "@mozilla.org/file/directory_service;1", "nsIProperties"],
880
["Env", "@mozilla.org/process/environment;1", "nsIEnvironment"],
881
["Favicon", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
882
["Form", "@mozilla.org/satchel/form-history;1", "nsIFormHistory2"],
883
["History", "@mozilla.org/browser/nav-history-service;1", "nsPIPlacesDatabase"],
884
["Idle", "@mozilla.org/widget/idleservice;1", "nsIIdleService"],
885
["IO", "@mozilla.org/network/io-service;1", "nsIIOService"],
886
["KeyFactory", "@mozilla.org/security/keyobjectfactory;1", "nsIKeyObjectFactory"],
887
["Login", "@mozilla.org/login-manager;1", "nsILoginManager"],
888
["Memory", "@mozilla.org/xpcom/memory-service;1", "nsIMemory"],
889
["Private", "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService"],
890
["Profiles", "@mozilla.org/toolkit/profile-service;1", "nsIToolkitProfileService"],
891
["Prompt", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
892
["Script", "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader"],
893
["SysInfo", "@mozilla.org/system-info;1", "nsIPropertyBag2"],
894
["Version", "@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator"],
895
["WinMediator", "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"],
896
["WinWatcher", "@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher"],
897
["Session", "@mozilla.org/browser/sessionstore;1", "nsISessionStore"],
898
].forEach(function(lazy) Utils.lazySvc(Svc, lazy[0], lazy[1], lazy[2]));
901
["engines", "errors", "sync"]
902
.forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy)));