2007-12-11 05:38:53 +00:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Bookmarks Sync.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2007
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Dan Mills <thunder@mozilla.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2009-08-19 03:03:11 +00:00
|
|
|
const EXPORTED_SYMBOLS = ['Utils', 'Svc', 'Str'];
|
2007-12-11 05:38:53 +00:00
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cr = Components.results;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
2009-02-10 08:52:05 +00:00
|
|
|
Cu.import("resource://weave/ext/Preferences.js");
|
|
|
|
Cu.import("resource://weave/ext/Observers.js");
|
2009-08-19 03:03:11 +00:00
|
|
|
Cu.import("resource://weave/ext/StringBundle.js");
|
2007-12-11 05:38:53 +00:00
|
|
|
Cu.import("resource://weave/constants.js");
|
|
|
|
Cu.import("resource://weave/log4moz.js");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Utility functions
|
|
|
|
*/
|
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
let Utils = {
|
2009-06-06 05:21:14 +00:00
|
|
|
/**
|
|
|
|
* Wrap a function to catch all exceptions and log them
|
|
|
|
*
|
|
|
|
* @usage MyObj._catch = Utils.catch;
|
|
|
|
* MyObj.foo = function() { this._catch(func)(); }
|
|
|
|
*/
|
|
|
|
catch: function Utils_catch(func) {
|
|
|
|
let thisArg = this;
|
|
|
|
return function WrappedCatch() {
|
|
|
|
try {
|
|
|
|
return func.call(thisArg);
|
|
|
|
}
|
|
|
|
catch(ex) {
|
2009-07-24 22:28:42 +00:00
|
|
|
thisArg._log.debug("Exception: " + Utils.exceptionStr(ex));
|
2009-06-06 05:21:14 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2009-06-06 18:52:49 +00:00
|
|
|
/**
|
|
|
|
* Wrap a function to call lock before calling the function then unlock.
|
|
|
|
*
|
|
|
|
* @usage MyObj._lock = Utils.lock;
|
|
|
|
* MyObj.foo = function() { this._lock(func)(); }
|
|
|
|
*/
|
|
|
|
lock: function Utils_lock(func) {
|
|
|
|
let thisArg = this;
|
|
|
|
return function WrappedLock() {
|
|
|
|
if (!thisArg.lock())
|
|
|
|
throw "Could not acquire lock";
|
|
|
|
|
|
|
|
try {
|
|
|
|
return func.call(thisArg);
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
thisArg.unlock();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2009-06-05 22:34:23 +00:00
|
|
|
/**
|
|
|
|
* Wrap functions to notify when it starts and finishes executing or if it got
|
|
|
|
* an error. The message is a combination of a provided prefix and local name
|
|
|
|
* with the current state and the subject is the provided subject.
|
|
|
|
*
|
|
|
|
* @usage function MyObj() { this._notify = Utils.notify("prefix:"); }
|
|
|
|
* MyObj.foo = function() { this._notify(name, subject, func)(); }
|
|
|
|
*/
|
|
|
|
notify: function Utils_notify(prefix) {
|
|
|
|
return function NotifyMaker(name, subject, func) {
|
|
|
|
let thisArg = this;
|
|
|
|
let notify = function(state) {
|
|
|
|
let mesg = prefix + name + ":" + state;
|
|
|
|
thisArg._log.debug("Event: " + mesg);
|
|
|
|
Observers.notify(mesg, subject);
|
|
|
|
};
|
|
|
|
|
|
|
|
return function WrappedNotify() {
|
|
|
|
try {
|
|
|
|
notify("start");
|
|
|
|
let ret = func.call(thisArg);
|
|
|
|
notify("finish");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
catch(ex) {
|
|
|
|
notify("error");
|
|
|
|
throw ex;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
},
|
2009-08-31 23:28:00 +00:00
|
|
|
|
|
|
|
batchSync: function batchSync(service, engineType) {
|
|
|
|
return function batchedSync() {
|
|
|
|
let engine = this;
|
|
|
|
let batchEx = null;
|
|
|
|
|
|
|
|
// Try running sync in batch mode
|
|
|
|
Svc[service].runInBatchMode({
|
|
|
|
runBatched: function wrappedSync() {
|
|
|
|
try {
|
|
|
|
engineType.prototype._sync.call(engine);
|
|
|
|
}
|
|
|
|
catch(ex) {
|
|
|
|
batchEx = ex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, null);
|
|
|
|
|
|
|
|
// Expose the exception if something inside the batch failed
|
|
|
|
if (batchEx!= null)
|
|
|
|
throw batchEx;
|
|
|
|
};
|
|
|
|
},
|
2009-06-05 22:34:23 +00:00
|
|
|
|
2008-06-20 19:34:29 +00:00
|
|
|
// Generates a brand-new globally unique identifier (GUID).
|
|
|
|
makeGUID: function makeGUID() {
|
2009-09-24 22:54:47 +00:00
|
|
|
// 70 characters that are not-escaped URL-friendly
|
|
|
|
const code =
|
|
|
|
"!()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
|
|
|
|
|
|
|
|
let guid = "";
|
|
|
|
let num = 0;
|
|
|
|
let val;
|
|
|
|
|
|
|
|
// Generate ten 70-value characters for a 70^10 (~61.29-bit) GUID
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
|
|
// Refresh the number source after using it a few times
|
|
|
|
if (i == 0 || i == 5)
|
|
|
|
num = Math.random();
|
|
|
|
|
|
|
|
// Figure out which code to use for the next GUID character
|
|
|
|
num *= 70;
|
|
|
|
val = Math.floor(num);
|
|
|
|
guid += code[val];
|
|
|
|
num -= val;
|
|
|
|
}
|
|
|
|
|
|
|
|
return guid;
|
2008-06-20 19:34:29 +00:00
|
|
|
},
|
|
|
|
|
2009-08-14 00:59:26 +00:00
|
|
|
anno: function anno(id, anno, val, expire) {
|
2009-08-21 21:29:37 +00:00
|
|
|
// Figure out if we have a bookmark or page
|
|
|
|
let annoFunc = (typeof id == "number" ? "Item" : "Page") + "Annotation";
|
|
|
|
|
|
|
|
// Convert to a nsIURI if necessary
|
|
|
|
if (typeof id == "string")
|
|
|
|
id = Utils.makeURI(id);
|
|
|
|
|
|
|
|
if (id == null)
|
|
|
|
throw "Null id for anno! (invalid uri)";
|
|
|
|
|
2009-08-14 00:59:26 +00:00
|
|
|
switch (arguments.length) {
|
|
|
|
case 2:
|
|
|
|
// Get the annotation with 2 args
|
2009-08-21 21:29:37 +00:00
|
|
|
return Svc.Annos["get" + annoFunc](id, anno);
|
2009-08-14 00:59:26 +00:00
|
|
|
case 3:
|
2009-08-21 21:29:37 +00:00
|
|
|
expire = "NEVER";
|
2009-08-14 00:59:26 +00:00
|
|
|
// Fallthrough!
|
|
|
|
case 4:
|
2009-08-21 21:29:37 +00:00
|
|
|
// Convert to actual EXPIRE value
|
|
|
|
expire = Svc.Annos["EXPIRE_" + expire];
|
|
|
|
|
2009-08-14 00:59:26 +00:00
|
|
|
// Set the annotation with 3 or 4 args
|
2009-08-21 21:29:37 +00:00
|
|
|
return Svc.Annos["set" + annoFunc](id, anno, val, 0, expire);
|
2009-08-14 00:59:26 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2009-09-18 07:15:37 +00:00
|
|
|
ensureOneOpen: let (windows = {}) function ensureOneOpen(window) {
|
|
|
|
// Close the other window if it exists
|
|
|
|
let url = window.location.href;
|
|
|
|
let other = windows[url];
|
|
|
|
if (other != null)
|
|
|
|
other.close();
|
|
|
|
|
|
|
|
// Save the new window for future closure
|
|
|
|
windows[url] = window;
|
|
|
|
|
|
|
|
// Actively clean up when the window is closed
|
|
|
|
window.addEventListener("unload", function() windows[url] = null, false);
|
|
|
|
},
|
|
|
|
|
2008-06-18 21:12:24 +00:00
|
|
|
// Returns a nsILocalFile representing a file relative to the
|
|
|
|
// current user's profile directory. If the argument is a string,
|
|
|
|
// it should be a string with unix-style slashes for directory names
|
|
|
|
// (these slashes are automatically converted to platform-specific
|
|
|
|
// path separators).
|
|
|
|
//
|
|
|
|
// Alternatively, if the argument is an object, it should contain
|
|
|
|
// the following attributes:
|
|
|
|
//
|
|
|
|
// path: the path to the file, relative to the current user's
|
|
|
|
// profile dir.
|
|
|
|
//
|
|
|
|
// autoCreate: whether or not the file should be created if it
|
|
|
|
// doesn't already exist.
|
|
|
|
getProfileFile: function getProfileFile(arg) {
|
|
|
|
if (typeof arg == "string")
|
|
|
|
arg = {path: arg};
|
|
|
|
|
|
|
|
let pathParts = arg.path.split("/");
|
2009-03-10 11:30:30 +00:00
|
|
|
let file = Svc.Directory.get("ProfD", Ci.nsIFile);
|
2008-06-18 21:12:24 +00:00
|
|
|
file.QueryInterface(Ci.nsILocalFile);
|
|
|
|
for (let i = 0; i < pathParts.length; i++)
|
|
|
|
file.append(pathParts[i]);
|
|
|
|
if (arg.autoCreate && !file.exists())
|
|
|
|
file.create(file.NORMAL_FILE_TYPE, PERMS_FILE);
|
|
|
|
return file;
|
|
|
|
},
|
|
|
|
|
2009-04-13 21:39:29 +00:00
|
|
|
/**
|
|
|
|
* Add a simple getter/setter to an object that defers access of a property
|
|
|
|
* to an inner property.
|
|
|
|
*
|
|
|
|
* @param obj
|
|
|
|
* Object to add properties to defer in its prototype
|
|
|
|
* @param defer
|
|
|
|
* Hash property of obj to defer to (dot split each level)
|
|
|
|
* @param prop
|
|
|
|
* Property name to defer (or an array of property names)
|
|
|
|
*/
|
|
|
|
deferGetSet: function Utils_deferGetSet(obj, defer, prop) {
|
|
|
|
if (Utils.isArray(prop))
|
|
|
|
return prop.map(function(prop) Utils.deferGetSet(obj, defer, prop));
|
|
|
|
|
|
|
|
// Split the defer into each dot part for each level to dereference
|
|
|
|
let parts = defer.split(".");
|
|
|
|
let deref = function(base) Utils.deref(base, parts);
|
|
|
|
|
|
|
|
let prot = obj.prototype;
|
|
|
|
|
|
|
|
// Create a getter if it doesn't exist yet
|
|
|
|
if (!prot.__lookupGetter__(prop))
|
|
|
|
prot.__defineGetter__(prop, function() deref(this)[prop]);
|
|
|
|
|
|
|
|
// Create a setter if it doesn't exist yet
|
|
|
|
if (!prot.__lookupSetter__(prop))
|
|
|
|
prot.__defineSetter__(prop, function(val) deref(this)[prop] = val);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dereference an array of properties starting from a base object
|
|
|
|
*
|
|
|
|
* @param base
|
|
|
|
* Base object to start dereferencing
|
|
|
|
* @param props
|
|
|
|
* Array of properties to dereference (one for each level)
|
|
|
|
*/
|
|
|
|
deref: function Utils_deref(base, props) props.reduce(function(curr, prop)
|
|
|
|
curr[prop], base),
|
|
|
|
|
2009-04-07 21:45:29 +00:00
|
|
|
/**
|
|
|
|
* Determine if some value is an array
|
|
|
|
*
|
|
|
|
* @param val
|
|
|
|
* Value to check (can be null, undefined, etc.)
|
|
|
|
* @return True if it's an array; false otherwise
|
|
|
|
*/
|
|
|
|
isArray: function Utils_isArray(val) val != null && typeof val == "object" &&
|
|
|
|
val.constructor.name == "Array",
|
|
|
|
|
2008-03-25 22:14:00 +00:00
|
|
|
// lazy load objects from a constructor on first access. It will
|
|
|
|
// work with the global object ('this' in the global context).
|
|
|
|
lazy: function Weave_lazy(dest, prop, ctr) {
|
2009-03-26 02:28:46 +00:00
|
|
|
delete dest[prop];
|
|
|
|
dest.__defineGetter__(prop, Utils.lazyCb(dest, prop, ctr));
|
|
|
|
},
|
|
|
|
lazyCb: function Weave_lazyCb(dest, prop, ctr) {
|
|
|
|
return function() {
|
2008-03-25 22:14:00 +00:00
|
|
|
delete dest[prop];
|
|
|
|
dest[prop] = new ctr();
|
|
|
|
return dest[prop];
|
|
|
|
};
|
|
|
|
},
|
2008-04-11 04:38:15 +00:00
|
|
|
|
2009-01-06 21:54:18 +00:00
|
|
|
// like lazy, but rather than new'ing the 3rd arg we use its return value
|
|
|
|
lazy2: function Weave_lazy2(dest, prop, fn) {
|
2009-03-26 02:28:46 +00:00
|
|
|
delete dest[prop];
|
|
|
|
dest.__defineGetter__(prop, Utils.lazyCb2(dest, prop, fn));
|
|
|
|
},
|
|
|
|
lazyCb2: function Weave_lazyCb2(dest, prop, fn) {
|
|
|
|
return function() {
|
2009-01-06 21:54:18 +00:00
|
|
|
delete dest[prop];
|
2009-03-20 09:35:46 +00:00
|
|
|
return dest[prop] = fn();
|
2009-01-06 21:54:18 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
lazySvc: function Weave_lazySvc(dest, prop, cid, iface) {
|
|
|
|
let getter = function() {
|
|
|
|
delete dest[prop];
|
2009-11-30 22:36:03 +00:00
|
|
|
let svc = null;
|
|
|
|
|
|
|
|
// Try creating a fake service if we can handle that
|
2009-03-11 00:12:59 +00:00
|
|
|
if (!Cc[cid]) {
|
2009-11-30 22:36:03 +00:00
|
|
|
switch (cid) {
|
|
|
|
case "@mozilla.org/privatebrowsing;1":
|
|
|
|
svc = {
|
|
|
|
autoStarted: false,
|
|
|
|
privateBrowsingEnabled: false
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-03-11 00:12:59 +00:00
|
|
|
let log = Log4Moz.repository.getLogger("Service.Util");
|
2009-11-30 22:36:03 +00:00
|
|
|
if (svc == null)
|
|
|
|
log.warn("Component " + cid + " doesn't exist on this platform.");
|
|
|
|
else
|
|
|
|
log.debug("Using a fake svc object for " + cid);
|
2009-03-11 00:12:59 +00:00
|
|
|
}
|
2009-11-30 22:36:03 +00:00
|
|
|
else
|
|
|
|
svc = Cc[cid].getService(iface);
|
|
|
|
|
|
|
|
return dest[prop] = svc;
|
2009-01-06 21:54:18 +00:00
|
|
|
};
|
|
|
|
dest.__defineGetter__(prop, getter);
|
|
|
|
},
|
|
|
|
|
|
|
|
lazyInstance: function Weave_lazyInstance(dest, prop, cid, iface) {
|
|
|
|
let getter = function() {
|
|
|
|
delete dest[prop];
|
|
|
|
dest[prop] = Cc[cid].createInstance(iface);
|
|
|
|
return dest[prop];
|
|
|
|
};
|
|
|
|
dest.__defineGetter__(prop, getter);
|
|
|
|
},
|
|
|
|
|
2009-08-19 03:03:11 +00:00
|
|
|
lazyStrings: function Weave_lazyStrings(name) {
|
|
|
|
let bundle = "chrome://weave/locale/" + name + ".properties";
|
|
|
|
return function() new StringBundle(bundle);
|
|
|
|
},
|
|
|
|
|
2009-05-19 15:53:30 +00:00
|
|
|
deepEquals: function eq(a, b) {
|
|
|
|
// If they're triple equals, then it must be equals!
|
|
|
|
if (a === b)
|
2008-02-13 22:30:44 +00:00
|
|
|
return true;
|
2008-03-05 08:00:56 +00:00
|
|
|
|
2009-07-23 06:49:15 +00:00
|
|
|
// If they weren't equal, they must be objects to be different
|
|
|
|
if (typeof a != "object" || typeof b != "object")
|
2008-02-13 22:30:44 +00:00
|
|
|
return false;
|
|
|
|
|
2009-07-23 06:49:15 +00:00
|
|
|
// But null objects won't have properties to compare
|
|
|
|
if (a === null || b === null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Make sure all of a's keys have a matching value in b
|
|
|
|
for (let k in a)
|
|
|
|
if (!eq(a[k], b[k]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Do the same for b's keys but skip those that we already checked
|
|
|
|
for (let k in b)
|
|
|
|
if (!(k in a) && !eq(a[k], b[k]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2008-02-13 22:30:44 +00:00
|
|
|
},
|
2007-12-11 05:38:53 +00:00
|
|
|
|
2008-07-23 22:46:48 +00:00
|
|
|
deepCopy: function Weave_deepCopy(thing, noSort) {
|
2008-03-05 08:00:56 +00:00
|
|
|
if (typeof(thing) != "object" || thing == null)
|
|
|
|
return thing;
|
|
|
|
let ret;
|
|
|
|
|
2009-04-07 21:45:29 +00:00
|
|
|
if (Utils.isArray(thing)) {
|
2008-03-05 08:00:56 +00:00
|
|
|
ret = [];
|
|
|
|
for (let i = 0; i < thing.length; i++)
|
2008-08-15 00:23:11 +00:00
|
|
|
ret.push(Utils.deepCopy(thing[i], noSort));
|
2008-03-05 08:00:56 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
ret = {};
|
2008-07-17 02:36:06 +00:00
|
|
|
let props = [p for (p in thing)];
|
2008-07-23 22:46:48 +00:00
|
|
|
if (!noSort)
|
2008-07-17 02:36:06 +00:00
|
|
|
props = props.sort();
|
2008-07-31 06:37:21 +00:00
|
|
|
props.forEach(function(k) ret[k] = Utils.deepCopy(thing[k], noSort));
|
2008-03-05 08:00:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
},
|
|
|
|
|
2008-07-11 20:40:06 +00:00
|
|
|
// Works on frames or exceptions, munges file:// URIs to shorten the paths
|
|
|
|
// FIXME: filename munging is sort of hackish, might be confusing if
|
|
|
|
// there are multiple extensions with similar filenames
|
|
|
|
formatFrame: function Utils_formatFrame(frame) {
|
|
|
|
let tmp = "<file:unknown>";
|
2008-11-07 01:34:34 +00:00
|
|
|
|
2009-07-24 22:28:42 +00:00
|
|
|
let file = frame.filename || frame.fileName;
|
|
|
|
if (file)
|
|
|
|
tmp = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1");
|
2008-11-07 01:34:34 +00:00
|
|
|
|
2008-07-11 20:40:06 +00:00
|
|
|
if (frame.lineNumber)
|
|
|
|
tmp += ":" + frame.lineNumber;
|
|
|
|
if (frame.name)
|
2009-07-24 22:28:42 +00:00
|
|
|
tmp = frame.name + "()@" + tmp;
|
2008-11-07 01:34:34 +00:00
|
|
|
|
2008-07-11 20:40:06 +00:00
|
|
|
return tmp;
|
|
|
|
},
|
|
|
|
|
2008-03-07 09:56:36 +00:00
|
|
|
exceptionStr: function Weave_exceptionStr(e) {
|
2008-06-26 18:15:02 +00:00
|
|
|
let message = e.message ? e.message : e;
|
2009-07-24 22:28:42 +00:00
|
|
|
return message + " " + Utils.stackTrace(e);
|
2008-07-11 20:40:06 +00:00
|
|
|
},
|
2008-03-07 09:56:36 +00:00
|
|
|
|
2009-07-24 22:28:42 +00:00
|
|
|
stackTraceFromFrame: function Weave_stackTraceFromFrame(frame) {
|
|
|
|
let output = [];
|
2008-07-01 19:03:05 +00:00
|
|
|
while (frame) {
|
2009-07-24 22:28:42 +00:00
|
|
|
let str = Utils.formatFrame(frame);
|
2008-11-07 01:34:34 +00:00
|
|
|
if (str)
|
2009-07-24 22:28:42 +00:00
|
|
|
output.push(str);
|
2008-07-01 19:03:05 +00:00
|
|
|
frame = frame.caller;
|
|
|
|
}
|
2009-07-24 22:28:42 +00:00
|
|
|
return output.join(" < ");
|
2008-07-01 19:03:05 +00:00
|
|
|
},
|
|
|
|
|
2009-07-24 22:28:42 +00:00
|
|
|
stackTrace: function Weave_stackTrace(e) {
|
|
|
|
// Wrapped nsIException
|
|
|
|
if (e.location)
|
|
|
|
return "Stack trace: " + Utils.stackTraceFromFrame(e.location);
|
|
|
|
|
|
|
|
// Standard JS exception
|
|
|
|
if (e.stack)
|
|
|
|
return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < ").
|
|
|
|
replace(/@(?:chrome|file):.*?([^\/\.]+\.\w+:)/g, "@$1");
|
|
|
|
|
|
|
|
return "No traceback available";
|
2008-03-07 09:56:36 +00:00
|
|
|
},
|
|
|
|
|
2008-03-05 08:00:56 +00:00
|
|
|
checkStatus: function Weave_checkStatus(code, msg, ranges) {
|
|
|
|
if (!ranges)
|
|
|
|
ranges = [[200,300]];
|
|
|
|
|
|
|
|
for (let i = 0; i < ranges.length; i++) {
|
2008-06-17 01:11:41 +00:00
|
|
|
var rng = ranges[i];
|
2008-03-05 08:00:56 +00:00
|
|
|
if (typeof(rng) == "object" && code >= rng[0] && code < rng[1])
|
2008-03-07 09:56:36 +00:00
|
|
|
return true;
|
2008-03-07 12:20:55 +00:00
|
|
|
else if (typeof(rng) == "number" && code == rng) {
|
2008-03-07 09:56:36 +00:00
|
|
|
return true;
|
2008-03-07 12:20:55 +00:00
|
|
|
}
|
2008-03-05 08:00:56 +00:00
|
|
|
}
|
|
|
|
|
2008-03-20 01:42:12 +00:00
|
|
|
if (msg) {
|
2008-11-03 22:56:56 +00:00
|
|
|
let log = Log4Moz.repository.getLogger("Service.Util");
|
2008-03-20 01:42:12 +00:00
|
|
|
log.error(msg + " Error code: " + code);
|
|
|
|
}
|
|
|
|
|
2008-03-07 09:56:36 +00:00
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
ensureStatus: function Weave_ensureStatus(args) {
|
|
|
|
if (!Utils.checkStatus.apply(Utils, arguments))
|
|
|
|
throw 'checkStatus failed';
|
2008-03-05 08:00:56 +00:00
|
|
|
},
|
|
|
|
|
2008-03-26 06:01:34 +00:00
|
|
|
sha1: function Weave_sha1(string) {
|
|
|
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
|
|
|
createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
|
|
converter.charset = "UTF-8";
|
|
|
|
|
|
|
|
let hasher = Cc["@mozilla.org/security/hash;1"]
|
|
|
|
.createInstance(Ci.nsICryptoHash);
|
|
|
|
hasher.init(hasher.SHA1);
|
|
|
|
|
|
|
|
let data = converter.convertToByteArray(string, {});
|
|
|
|
hasher.update(data, data.length);
|
|
|
|
let rawHash = hasher.finish(false);
|
|
|
|
|
|
|
|
// return the two-digit hexadecimal code for a byte
|
|
|
|
function toHexString(charCode) {
|
|
|
|
return ("0" + charCode.toString(16)).slice(-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
let hash = [toHexString(rawHash.charCodeAt(i)) for (i in rawHash)].join("");
|
|
|
|
return hash;
|
|
|
|
},
|
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
makeURI: function Weave_makeURI(URIString) {
|
2009-01-07 22:22:02 +00:00
|
|
|
if (!URIString)
|
2008-02-13 22:30:44 +00:00
|
|
|
return null;
|
2008-11-03 22:56:56 +00:00
|
|
|
try {
|
2009-01-07 22:22:02 +00:00
|
|
|
return Svc.IO.newURI(URIString, null, null);
|
2008-11-03 22:56:56 +00:00
|
|
|
} catch (e) {
|
|
|
|
let log = Log4Moz.repository.getLogger("Service.Util");
|
2009-07-24 22:28:42 +00:00
|
|
|
log.debug("Could not create URI: " + Utils.exceptionStr(e));
|
2008-11-03 22:56:56 +00:00
|
|
|
return null;
|
|
|
|
}
|
2008-02-13 22:30:44 +00:00
|
|
|
},
|
2007-12-11 05:38:53 +00:00
|
|
|
|
2009-02-12 03:19:04 +00:00
|
|
|
makeURL: function Weave_makeURL(URIString) {
|
|
|
|
let url = Utils.makeURI(URIString);
|
|
|
|
url.QueryInterface(Ci.nsIURL);
|
|
|
|
return url;
|
|
|
|
},
|
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
xpath: function Weave_xpath(xmlDoc, xpathString) {
|
|
|
|
let root = xmlDoc.ownerDocument == null ?
|
2008-05-23 18:05:42 +00:00
|
|
|
xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement;
|
2008-02-13 22:30:44 +00:00
|
|
|
let nsResolver = xmlDoc.createNSResolver(root);
|
2007-12-11 05:38:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
return xmlDoc.evaluate(xpathString, xmlDoc, nsResolver,
|
|
|
|
Ci.nsIDOMXPathResult.ANY_TYPE, null);
|
|
|
|
},
|
2007-12-11 05:38:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
getTmp: function Weave_getTmp(name) {
|
2009-03-10 11:30:30 +00:00
|
|
|
let tmp = Svc.Directory.get("ProfD", Ci.nsIFile);
|
2008-02-13 22:30:44 +00:00
|
|
|
tmp.QueryInterface(Ci.nsILocalFile);
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
tmp.append("weave");
|
|
|
|
tmp.append("tmp");
|
|
|
|
if (!tmp.exists())
|
|
|
|
tmp.create(tmp.DIRECTORY_TYPE, PERMS_DIRECTORY);
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
if (name)
|
|
|
|
tmp.append(name);
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
return tmp;
|
|
|
|
},
|
2008-04-11 04:38:15 +00:00
|
|
|
|
2009-04-01 00:52:16 +00:00
|
|
|
/**
|
|
|
|
* Load a json object from disk
|
|
|
|
*
|
|
|
|
* @param filePath
|
|
|
|
* Json file path load from weave/[filePath].json
|
|
|
|
* @param that
|
|
|
|
* Object to use for logging and "this" for callback
|
|
|
|
* @param callback
|
|
|
|
* Function to process json object as its first parameter
|
|
|
|
*/
|
|
|
|
jsonLoad: function Utils_jsonLoad(filePath, that, callback) {
|
|
|
|
filePath = "weave/" + filePath + ".json";
|
|
|
|
if (that._log)
|
2009-07-15 01:44:41 +00:00
|
|
|
that._log.trace("Loading json from disk: " + filePath);
|
2009-04-01 00:52:16 +00:00
|
|
|
|
|
|
|
let file = Utils.getProfileFile(filePath);
|
|
|
|
if (!file.exists())
|
|
|
|
return;
|
|
|
|
|
|
|
|
try {
|
|
|
|
let [is] = Utils.open(file, "<");
|
|
|
|
let json = Utils.readStream(is);
|
|
|
|
is.close();
|
|
|
|
callback.call(that, JSON.parse(json));
|
|
|
|
}
|
|
|
|
catch (ex) {
|
|
|
|
if (that._log)
|
2009-07-24 22:28:42 +00:00
|
|
|
that._log.debug("Failed to load json: " + Utils.exceptionStr(ex));
|
2009-04-01 00:52:16 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save a json-able object to disk
|
|
|
|
*
|
|
|
|
* @param filePath
|
|
|
|
* Json file path save to weave/[filePath].json
|
|
|
|
* @param that
|
|
|
|
* Object to use for logging and "this" for callback
|
|
|
|
* @param callback
|
|
|
|
* Function to provide json-able object to save. If this isn't a
|
|
|
|
* function, it'll be used as the object to make a json string.
|
|
|
|
*/
|
|
|
|
jsonSave: function Utils_jsonSave(filePath, that, callback) {
|
|
|
|
filePath = "weave/" + filePath + ".json";
|
|
|
|
if (that._log)
|
2009-07-15 01:44:41 +00:00
|
|
|
that._log.trace("Saving json to disk: " + filePath);
|
2009-04-01 00:52:16 +00:00
|
|
|
|
|
|
|
let file = Utils.getProfileFile({ autoCreate: true, path: filePath });
|
|
|
|
let json = typeof callback == "function" ? callback.call(that) : callback;
|
|
|
|
let out = JSON.stringify(json);
|
|
|
|
let [fos] = Utils.open(file, ">");
|
|
|
|
fos.writeString(out);
|
|
|
|
fos.close();
|
|
|
|
},
|
|
|
|
|
2009-09-25 05:51:38 +00:00
|
|
|
/**
|
|
|
|
* Return a timer that is scheduled to call the callback after waiting the
|
|
|
|
* provided time or as soon as possible. The timer will be set as a property
|
|
|
|
* of the provided object with the given timer name.
|
|
|
|
*/
|
|
|
|
delay: function delay(callback, wait, thisObj, name) {
|
|
|
|
// Default to running right away
|
|
|
|
wait = wait || 0;
|
|
|
|
|
|
|
|
// Use a dummy object if one wasn't provided
|
|
|
|
thisObj = thisObj || {};
|
|
|
|
|
|
|
|
// Delay an existing timer if it exists
|
|
|
|
if (thisObj[name] instanceof Ci.nsITimer) {
|
|
|
|
thisObj[name].delay = wait;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a special timer that we can add extra properties
|
|
|
|
let timer = {};
|
|
|
|
timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
|
|
|
|
|
|
// Provide an easy way to clear out the timer
|
|
|
|
timer.clear = function() {
|
|
|
|
thisObj[name] = null;
|
|
|
|
timer.cancel();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Initialize the timer with a smart callback
|
|
|
|
timer.initWithCallback({
|
|
|
|
notify: function notify() {
|
|
|
|
// Clear out the timer once it's been triggered
|
|
|
|
timer.clear();
|
|
|
|
callback.call(thisObj, timer);
|
|
|
|
}
|
|
|
|
}, wait, timer.TYPE_ONE_SHOT);
|
|
|
|
|
|
|
|
return thisObj[name] = timer;
|
2008-06-10 02:30:11 +00:00
|
|
|
},
|
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
open: function open(pathOrFile, mode, perms) {
|
|
|
|
let stream, file;
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
if (pathOrFile instanceof Ci.nsIFile) {
|
|
|
|
file = pathOrFile;
|
|
|
|
} else {
|
|
|
|
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
|
|
|
dump("PATH IS" + pathOrFile + "\n");
|
|
|
|
file.initWithPath(pathOrFile);
|
|
|
|
}
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
if (!perms)
|
|
|
|
perms = PERMS_FILE;
|
|
|
|
|
|
|
|
switch(mode) {
|
|
|
|
case "<": {
|
|
|
|
if (!file.exists())
|
|
|
|
throw "Cannot open file for reading, file does not exist";
|
2008-02-20 04:53:01 +00:00
|
|
|
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
|
2008-02-13 22:30:44 +00:00
|
|
|
createInstance(Ci.nsIFileInputStream);
|
2008-02-20 04:53:01 +00:00
|
|
|
fis.init(file, MODE_RDONLY, perms, 0);
|
2008-03-05 08:00:56 +00:00
|
|
|
stream = Cc["@mozilla.org/intl/converter-input-stream;1"].
|
|
|
|
createInstance(Ci.nsIConverterInputStream);
|
|
|
|
stream.init(fis, "UTF-8", 4096,
|
|
|
|
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
2008-02-13 22:30:44 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case ">": {
|
2008-03-05 08:00:56 +00:00
|
|
|
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
|
2008-02-13 22:30:44 +00:00
|
|
|
createInstance(Ci.nsIFileOutputStream);
|
2008-03-05 08:00:56 +00:00
|
|
|
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, perms, 0);
|
|
|
|
stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
|
|
|
|
.createInstance(Ci.nsIConverterOutputStream);
|
|
|
|
stream.init(fos, "UTF-8", 4096, 0x0000);
|
2008-02-13 22:30:44 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case ">>": {
|
2008-03-05 08:00:56 +00:00
|
|
|
let fos = Cc["@mozilla.org/network/file-output-stream;1"].
|
2008-02-13 22:30:44 +00:00
|
|
|
createInstance(Ci.nsIFileOutputStream);
|
2008-03-05 08:00:56 +00:00
|
|
|
fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_APPEND, perms, 0);
|
|
|
|
stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
|
|
|
|
.createInstance(Ci.nsIConverterOutputStream);
|
|
|
|
stream.init(fos, "UTF-8", 4096, 0x0000);
|
2008-02-13 22:30:44 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw "Illegal mode to open(): " + mode;
|
|
|
|
}
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2008-02-13 22:30:44 +00:00
|
|
|
return [stream, file];
|
|
|
|
},
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2009-03-19 21:00:57 +00:00
|
|
|
/**
|
|
|
|
* Open/reshow a window/dialog based on its name and type.
|
|
|
|
*
|
|
|
|
* @param name
|
|
|
|
* Name of the window/dialog to reshow if already open
|
|
|
|
* @param type
|
|
|
|
* Opening behavior: "Window" or "Dialog"
|
|
|
|
* @param args
|
|
|
|
* More arguments go here depending on the type
|
|
|
|
*/
|
|
|
|
_openWin: function Utils__openWin(name, type /*, args... */) {
|
|
|
|
// Just re-show the window if it's already open
|
|
|
|
let openedWindow = Svc.WinMediator.getMostRecentWindow("Weave:" + name);
|
|
|
|
if (openedWindow) {
|
|
|
|
openedWindow.focus();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open up the window/dialog!
|
|
|
|
let win = Svc.WinWatcher;
|
|
|
|
if (type == "Dialog")
|
|
|
|
win = win.activeWindow;
|
2009-03-20 09:35:46 +00:00
|
|
|
win["open" + type].apply(win, Array.slice(arguments, 2));
|
2009-03-19 21:21:23 +00:00
|
|
|
},
|
2009-08-19 03:03:11 +00:00
|
|
|
|
2009-07-28 21:30:32 +00:00
|
|
|
_openChromeWindow: function Utils_openCWindow(name, uri, options, args) {
|
|
|
|
Utils.openWindow(name, "chrome://weave/content/" + uri, options, args);
|
|
|
|
},
|
2009-08-19 03:03:11 +00:00
|
|
|
|
2009-07-28 21:30:32 +00:00
|
|
|
openWindow: function Utils_openWindow(name, uri, options, args) {
|
|
|
|
Utils._openWin(name, "Window", null, uri, "",
|
2009-08-19 03:03:11 +00:00
|
|
|
options || "centerscreen,chrome,dialog,resizable=yes", args);
|
2009-07-28 21:30:32 +00:00
|
|
|
},
|
2009-08-19 03:03:11 +00:00
|
|
|
|
2009-03-19 21:21:23 +00:00
|
|
|
openDialog: function Utils_openDialog(name, uri, options, args) {
|
|
|
|
Utils._openWin(name, "Dialog", "chrome://weave/content/" + uri, "",
|
2009-03-26 02:28:46 +00:00
|
|
|
options || "centerscreen,chrome,dialog,modal,resizable=no", args);
|
2009-03-19 21:21:23 +00:00
|
|
|
},
|
|
|
|
|
2009-10-23 05:10:36 +00:00
|
|
|
openGenericDialog: function Utils_openGenericDialog(type) {
|
|
|
|
this._genericDialogType = type;
|
|
|
|
this.openDialog("ChangeSomething", "generic-change.xul");
|
|
|
|
},
|
|
|
|
|
2009-11-26 00:59:02 +00:00
|
|
|
getIcon: function(iconUri, defaultIcon) {
|
|
|
|
try {
|
|
|
|
let iconURI = Utils.makeURI(iconUri);
|
|
|
|
return Svc.Favicon.getFaviconLinkForIcon(iconURI).spec;
|
|
|
|
}
|
|
|
|
catch(ex) {}
|
|
|
|
|
|
|
|
// Just give the provided default icon or the system's default
|
|
|
|
return defaultIcon || Svc.Favicon.defaultFavicon.spec;
|
|
|
|
},
|
|
|
|
|
2009-08-25 04:47:35 +00:00
|
|
|
getErrorString: function Utils_getErrorString(error, args) {
|
2009-09-16 01:38:52 +00:00
|
|
|
try {
|
2009-09-16 23:52:43 +00:00
|
|
|
return Str.errors.get(error, args || null);
|
2009-09-16 01:38:52 +00:00
|
|
|
} catch (e) {}
|
2009-11-20 22:34:20 +00:00
|
|
|
|
2009-09-16 01:38:52 +00:00
|
|
|
// basically returns "Unknown Error"
|
2009-09-16 23:52:43 +00:00
|
|
|
return Str.errors.get("error.reason.unknown");
|
2009-08-25 04:47:35 +00:00
|
|
|
},
|
|
|
|
|
2008-03-05 08:00:56 +00:00
|
|
|
// assumes an nsIConverterInputStream
|
2008-02-20 04:53:01 +00:00
|
|
|
readStream: function Weave_readStream(is) {
|
2008-03-05 08:00:56 +00:00
|
|
|
let ret = "", str = {};
|
|
|
|
while (is.readString(4096, str) != 0) {
|
|
|
|
ret += str.value;
|
2008-02-13 22:30:44 +00:00
|
|
|
}
|
2008-02-20 04:53:01 +00:00
|
|
|
return ret;
|
2008-02-13 22:30:44 +00:00
|
|
|
},
|
2008-02-05 22:15:53 +00:00
|
|
|
|
2009-09-11 06:11:33 +00:00
|
|
|
/**
|
|
|
|
* Create an array like the first but without elements of the second
|
|
|
|
*/
|
|
|
|
arraySub: function arraySub(minuend, subtrahend) {
|
|
|
|
return minuend.filter(function(i) subtrahend.indexOf(i) == -1);
|
|
|
|
},
|
|
|
|
|
2008-03-07 09:56:36 +00:00
|
|
|
bind2: function Async_bind2(object, method) {
|
2008-05-23 18:05:42 +00:00
|
|
|
return function innerBind() { return method.apply(object, arguments); };
|
2008-03-07 09:56:36 +00:00
|
|
|
},
|
|
|
|
|
2008-06-17 18:11:56 +00:00
|
|
|
__prefs: null,
|
2008-03-19 22:17:04 +00:00
|
|
|
get prefs() {
|
|
|
|
if (!this.__prefs) {
|
|
|
|
this.__prefs = Cc["@mozilla.org/preferences-service;1"]
|
|
|
|
.getService(Ci.nsIPrefService);
|
|
|
|
this.__prefs = this.__prefs.getBranch(PREFS_BRANCH);
|
|
|
|
this.__prefs.QueryInterface(Ci.nsIPrefBranch2);
|
|
|
|
}
|
|
|
|
return this.__prefs;
|
2008-02-05 22:15:53 +00:00
|
|
|
}
|
2009-01-06 21:54:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commonly-used services
|
|
|
|
*/
|
|
|
|
|
|
|
|
let Svc = {};
|
2009-02-10 08:52:05 +00:00
|
|
|
Svc.Prefs = new Preferences(PREFS_BRANCH);
|
2009-12-15 22:21:12 +00:00
|
|
|
Svc.Obs = Observers;
|
2009-08-14 00:59:26 +00:00
|
|
|
[["Annos", "@mozilla.org/browser/annotation-service;1", "nsIAnnotationService"],
|
|
|
|
["AppInfo", "@mozilla.org/xre/app-info;1", "nsIXULAppInfo"],
|
2009-06-17 16:28:15 +00:00
|
|
|
["Bookmark", "@mozilla.org/browser/nav-bookmarks-service;1", "nsINavBookmarksService"],
|
2009-04-08 07:12:57 +00:00
|
|
|
["Crypto", "@labs.mozilla.com/Weave/Crypto;1", "IWeaveCrypto"],
|
2009-03-19 21:00:57 +00:00
|
|
|
["Directory", "@mozilla.org/file/directory_service;1", "nsIProperties"],
|
2009-11-20 07:31:04 +00:00
|
|
|
["Env", "@mozilla.org/process/environment;1", "nsIEnvironment"],
|
2009-11-26 00:59:02 +00:00
|
|
|
["Favicon", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
|
2009-10-16 23:19:28 +00:00
|
|
|
["History", "@mozilla.org/browser/nav-history-service;1", "nsPIPlacesDatabase"],
|
2009-07-17 23:55:15 +00:00
|
|
|
["Idle", "@mozilla.org/widget/idleservice;1", "nsIIdleService"],
|
2009-03-19 21:00:57 +00:00
|
|
|
["IO", "@mozilla.org/network/io-service;1", "nsIIOService"],
|
|
|
|
["Login", "@mozilla.org/login-manager;1", "nsILoginManager"],
|
|
|
|
["Memory", "@mozilla.org/xpcom/memory-service;1", "nsIMemory"],
|
|
|
|
["Observer", "@mozilla.org/observer-service;1", "nsIObserverService"],
|
|
|
|
["Private", "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService"],
|
2009-11-20 07:31:04 +00:00
|
|
|
["Profiles", "@mozilla.org/toolkit/profile-service;1", "nsIToolkitProfileService"],
|
2009-03-19 21:00:57 +00:00
|
|
|
["Prompt", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
|
2009-10-22 23:15:34 +00:00
|
|
|
["Script", "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader"],
|
2009-11-20 07:31:04 +00:00
|
|
|
["SysInfo", "@mozilla.org/system-info;1", "nsIPropertyBag2"],
|
2009-03-19 21:00:57 +00:00
|
|
|
["Version", "@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator"],
|
|
|
|
["WinMediator", "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"],
|
|
|
|
["WinWatcher", "@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher"],
|
|
|
|
].forEach(function(lazy) Utils.lazySvc(Svc, lazy[0], lazy[1], Ci[lazy[2]]));
|
2009-08-19 03:03:11 +00:00
|
|
|
|
|
|
|
let Str = {};
|
2009-10-28 07:42:35 +00:00
|
|
|
["engines", "errors", "sync"]
|
2009-08-19 03:03:11 +00:00
|
|
|
.forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy)));
|