~ubuntu-branches/ubuntu/oneiric/weave/oneiric

« back to all changes in this revision

Viewing changes to source/modules/util.js

  • Committer: Bazaar Package Importer
  • Author(s): Micah Gersten
  • Date: 2010-08-11 00:35:15 UTC
  • mfrom: (3.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100811003515-o3jbh826bnd1syjv
Tags: 1.4.3-1ubuntu1
* Add -fshort-wchar to CXXFLAGS to fix FTBFS in Ubuntu
  - update debian/rules 

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 Bookmarks Sync.
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
 
 *  Dan Mills <thunder@mozilla.com>
22
 
 *
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.
34
 
 *
35
 
 * ***** END LICENSE BLOCK ***** */
36
 
 
37
 
const EXPORTED_SYMBOLS = ['Utils', 'Svc', 'Str'];
38
 
 
39
 
const Cc = Components.classes;
40
 
const Ci = Components.interfaces;
41
 
const Cr = Components.results;
42
 
const Cu = Components.utils;
43
 
 
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");
49
 
 
50
 
/*
51
 
 * Utility functions
52
 
 */
53
 
 
54
 
let Utils = {
55
 
  /**
56
 
   * Wrap a function to catch all exceptions and log them
57
 
   *
58
 
   * @usage MyObj._catch = Utils.catch;
59
 
   *        MyObj.foo = function() { this._catch(func)(); }
60
 
   */
61
 
  catch: function Utils_catch(func) {
62
 
    let thisArg = this;
63
 
    return function WrappedCatch() {
64
 
      try {
65
 
        return func.call(thisArg);
66
 
      }
67
 
      catch(ex) {
68
 
        thisArg._log.debug("Exception: " + Utils.exceptionStr(ex));
69
 
      }
70
 
    };
71
 
  },
72
 
 
73
 
  /**
74
 
   * Wrap a function to call lock before calling the function then unlock.
75
 
   *
76
 
   * @usage MyObj._lock = Utils.lock;
77
 
   *        MyObj.foo = function() { this._lock(func)(); }
78
 
   */
79
 
  lock: function Utils_lock(func) {
80
 
    let thisArg = this;
81
 
    return function WrappedLock() {
82
 
      if (!thisArg.lock())
83
 
        throw "Could not acquire lock";
84
 
 
85
 
      try {
86
 
        return func.call(thisArg);
87
 
      }
88
 
      finally {
89
 
        thisArg.unlock();
90
 
      }
91
 
    };
92
 
  },
93
 
 
94
 
  /**
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.
98
 
   *
99
 
   * @usage function MyObj() { this._notify = Utils.notify("prefix:"); }
100
 
   *        MyObj.foo = function() { this._notify(name, subject, func)(); }
101
 
   */
102
 
  notify: function Utils_notify(prefix) {
103
 
    return function NotifyMaker(name, subject, func) {
104
 
      let thisArg = this;
105
 
      let notify = function(state) {
106
 
        let mesg = prefix + name + ":" + state;
107
 
        thisArg._log.trace("Event: " + mesg);
108
 
        Observers.notify(mesg, subject);
109
 
      };
110
 
 
111
 
      return function WrappedNotify() {
112
 
        try {
113
 
          notify("start");
114
 
          let ret = func.call(thisArg);
115
 
          notify("finish");
116
 
          return ret;
117
 
        }
118
 
        catch(ex) {
119
 
          notify("error");
120
 
          throw ex;
121
 
        }
122
 
      };
123
 
    };
124
 
  },
125
 
 
126
 
  batchSync: function batchSync(service, engineType) {
127
 
    return function batchedSync() {
128
 
      let engine = this;
129
 
      let batchEx = null;
130
 
 
131
 
      // Try running sync in batch mode
132
 
      Svc[service].runInBatchMode({
133
 
        runBatched: function wrappedSync() {
134
 
          try {
135
 
            engineType.prototype._sync.call(engine);
136
 
          }
137
 
          catch(ex) {
138
 
            batchEx = ex;
139
 
          }
140
 
        }
141
 
      }, null);
142
 
 
143
 
      // Expose the exception if something inside the batch failed
144
 
      if (batchEx!= null)
145
 
        throw batchEx;
146
 
    };
147
 
  },
148
 
 
149
 
  // Generates a brand-new globally unique identifier (GUID).
150
 
  makeGUID: function makeGUID() {
151
 
    // 70 characters that are not-escaped URL-friendly
152
 
    const code =
153
 
      "!()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
154
 
 
155
 
    let guid = "";
156
 
    let num = 0;
157
 
    let val;
158
 
 
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)
163
 
        num = Math.random();
164
 
 
165
 
      // Figure out which code to use for the next GUID character
166
 
      num *= 70;
167
 
      val = Math.floor(num);
168
 
      guid += code[val];
169
 
      num -= val;
170
 
    }
171
 
 
172
 
    return guid;
173
 
  },
174
 
 
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";
178
 
 
179
 
    // Convert to a nsIURI if necessary
180
 
    if (typeof id == "string")
181
 
      id = Utils.makeURI(id);
182
 
 
183
 
    if (id == null)
184
 
      throw "Null id for anno! (invalid uri)";
185
 
 
186
 
    switch (arguments.length) {
187
 
      case 2:
188
 
        // Get the annotation with 2 args
189
 
        return Svc.Annos["get" + annoFunc](id, anno);
190
 
      case 3:
191
 
        expire = "NEVER";
192
 
        // Fallthrough!
193
 
      case 4:
194
 
        // Convert to actual EXPIRE value
195
 
        expire = Svc.Annos["EXPIRE_" + expire];
196
 
 
197
 
        // Set the annotation with 3 or 4 args
198
 
        return Svc.Annos["set" + annoFunc](id, anno, val, 0, expire);
199
 
    }
200
 
  },
201
 
 
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];
206
 
    if (other != null)
207
 
      other.close();
208
 
 
209
 
    // Save the new window for future closure
210
 
    windows[url] = window;
211
 
 
212
 
    // Actively clean up when the window is closed
213
 
    window.addEventListener("unload", function() windows[url] = null, false);
214
 
  },
215
 
 
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
220
 
  // path separators).
221
 
  //
222
 
  // Alternatively, if the argument is an object, it should contain
223
 
  // the following attributes:
224
 
  //
225
 
  //   path: the path to the file, relative to the current user's
226
 
  //   profile dir.
227
 
  //
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")
232
 
      arg = {path: arg};
233
 
 
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);
241
 
    return file;
242
 
  },
243
 
 
244
 
  /**
245
 
   * Add a simple getter/setter to an object that defers access of a property
246
 
   * to an inner property.
247
 
   *
248
 
   * @param obj
249
 
   *        Object to add properties to defer in its prototype
250
 
   * @param defer
251
 
   *        Hash property of obj to defer to (dot split each level)
252
 
   * @param prop
253
 
   *        Property name to defer (or an array of property names)
254
 
   */
255
 
  deferGetSet: function Utils_deferGetSet(obj, defer, prop) {
256
 
    if (Utils.isArray(prop))
257
 
      return prop.map(function(prop) Utils.deferGetSet(obj, defer, prop));
258
 
 
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);
262
 
 
263
 
    let prot = obj.prototype;
264
 
 
265
 
    // Create a getter if it doesn't exist yet
266
 
    if (!prot.__lookupGetter__(prop))
267
 
      prot.__defineGetter__(prop, function() deref(this)[prop]);
268
 
 
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);
272
 
  },
273
 
 
274
 
  /**
275
 
   * Dereference an array of properties starting from a base object
276
 
   *
277
 
   * @param base
278
 
   *        Base object to start dereferencing
279
 
   * @param props
280
 
   *        Array of properties to dereference (one for each level)
281
 
   */
282
 
  deref: function Utils_deref(base, props) props.reduce(function(curr, prop)
283
 
    curr[prop], base),
284
 
 
285
 
  /**
286
 
   * Determine if some value is an array
287
 
   *
288
 
   * @param val
289
 
   *        Value to check (can be null, undefined, etc.)
290
 
   * @return True if it's an array; false otherwise
291
 
   */
292
 
  isArray: function Utils_isArray(val) val != null && typeof val == "object" &&
293
 
    val.constructor.name == "Array",
294
 
 
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) {
298
 
    delete dest[prop];
299
 
    dest.__defineGetter__(prop, Utils.lazyCb(dest, prop, ctr));
300
 
  },
301
 
  lazyCb: function Weave_lazyCb(dest, prop, ctr) {
302
 
    return function() {
303
 
      delete dest[prop];
304
 
      dest[prop] = new ctr();
305
 
      return dest[prop];
306
 
    };
307
 
  },
308
 
 
309
 
  // like lazy, but rather than new'ing the 3rd arg we use its return value
310
 
  lazy2: function Weave_lazy2(dest, prop, fn) {
311
 
    delete dest[prop];
312
 
    dest.__defineGetter__(prop, Utils.lazyCb2(dest, prop, fn));
313
 
  },
314
 
  lazyCb2: function Weave_lazyCb2(dest, prop, fn) {
315
 
    return function() {
316
 
      delete dest[prop];
317
 
      return dest[prop] = fn();
318
 
    };
319
 
  },
320
 
 
321
 
  lazySvc: function Weave_lazySvc(dest, prop, cid, iface) {
322
 
    let getter = function() {
323
 
      delete dest[prop];
324
 
      let svc = null;
325
 
 
326
 
      // Use the platform's service if it exists
327
 
      if (cid in Cc && iface in Ci)
328
 
        svc = Cc[cid].getService(Ci[iface]);
329
 
      else {
330
 
        svc = FakeSvc[cid];
331
 
 
332
 
        let log = Log4Moz.repository.getLogger("Service.Util");
333
 
        if (svc == null)
334
 
          log.warn("Component " + cid + " doesn't exist on this platform.");
335
 
        else
336
 
          log.debug("Using a fake svc object for " + cid);
337
 
      }
338
 
 
339
 
      return dest[prop] = svc;
340
 
    };
341
 
    dest.__defineGetter__(prop, getter);
342
 
  },
343
 
 
344
 
  lazyStrings: function Weave_lazyStrings(name) {
345
 
    let bundle = "chrome://weave/locale/" + name + ".properties";
346
 
    return function() new StringBundle(bundle);
347
 
  },
348
 
 
349
 
  deepEquals: function eq(a, b) {
350
 
    // If they're triple equals, then it must be equals!
351
 
    if (a === b)
352
 
      return true;
353
 
 
354
 
    // If they weren't equal, they must be objects to be different
355
 
    if (typeof a != "object" || typeof b != "object")
356
 
      return false;
357
 
 
358
 
    // But null objects won't have properties to compare
359
 
    if (a === null || b === null)
360
 
      return false;
361
 
 
362
 
    // Make sure all of a's keys have a matching value in b
363
 
    for (let k in a)
364
 
      if (!eq(a[k], b[k]))
365
 
        return false;
366
 
 
367
 
    // Do the same for b's keys but skip those that we already checked
368
 
    for (let k in b)
369
 
      if (!(k in a) && !eq(a[k], b[k]))
370
 
        return false;
371
 
 
372
 
    return true;
373
 
  },
374
 
 
375
 
  deepCopy: function Weave_deepCopy(thing, noSort) {
376
 
    if (typeof(thing) != "object" || thing == null)
377
 
      return thing;
378
 
    let ret;
379
 
 
380
 
    if (Utils.isArray(thing)) {
381
 
      ret = [];
382
 
      for (let i = 0; i < thing.length; i++)
383
 
        ret.push(Utils.deepCopy(thing[i], noSort));
384
 
 
385
 
    } else {
386
 
      ret = {};
387
 
      let props = [p for (p in thing)];
388
 
      if (!noSort)
389
 
        props = props.sort();
390
 
      props.forEach(function(k) ret[k] = Utils.deepCopy(thing[k], noSort));
391
 
    }
392
 
 
393
 
    return ret;
394
 
  },
395
 
 
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>";
401
 
 
402
 
    let file = frame.filename || frame.fileName;
403
 
    if (file)
404
 
      tmp = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1");
405
 
 
406
 
    if (frame.lineNumber)
407
 
      tmp += ":" + frame.lineNumber;
408
 
    if (frame.name)
409
 
      tmp = frame.name + "()@" + tmp;
410
 
 
411
 
    return tmp;
412
 
  },
413
 
 
414
 
  exceptionStr: function Weave_exceptionStr(e) {
415
 
    let message = e.message ? e.message : e;
416
 
    return message + " " + Utils.stackTrace(e);
417
 
 },
418
 
 
419
 
  stackTraceFromFrame: function Weave_stackTraceFromFrame(frame) {
420
 
    let output = [];
421
 
    while (frame) {
422
 
      let str = Utils.formatFrame(frame);
423
 
      if (str)
424
 
        output.push(str);
425
 
      frame = frame.caller;
426
 
    }
427
 
    return output.join(" < ");
428
 
  },
429
 
 
430
 
  stackTrace: function Weave_stackTrace(e) {
431
 
    // Wrapped nsIException
432
 
    if (e.location)
433
 
      return "Stack trace: " + Utils.stackTraceFromFrame(e.location);
434
 
 
435
 
    // Standard JS exception
436
 
    if (e.stack)
437
 
      return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < ").
438
 
        replace(/@(?:chrome|file):.*?([^\/\.]+\.\w+:)/g, "@$1");
439
 
 
440
 
    return "No traceback available";
441
 
  },
442
 
 
443
 
  checkStatus: function Weave_checkStatus(code, msg, ranges) {
444
 
    if (!ranges)
445
 
      ranges = [[200,300]];
446
 
 
447
 
    for (let i = 0; i < ranges.length; i++) {
448
 
      var rng = ranges[i];
449
 
      if (typeof(rng) == "object" && code >= rng[0] && code < rng[1])
450
 
        return true;
451
 
      else if (typeof(rng) == "number" && code == rng) {
452
 
        return true;
453
 
      }
454
 
    }
455
 
 
456
 
    if (msg) {
457
 
      let log = Log4Moz.repository.getLogger("Service.Util");
458
 
      log.error(msg + " Error code: " + code);
459
 
    }
460
 
 
461
 
    return false;
462
 
  },
463
 
 
464
 
  ensureStatus: function Weave_ensureStatus(args) {
465
 
    if (!Utils.checkStatus.apply(Utils, arguments))
466
 
      throw 'checkStatus failed';
467
 
  },
468
 
 
469
 
  digest: function digest(message, hasher) {
470
 
    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
471
 
      createInstance(Ci.nsIScriptableUnicodeConverter);
472
 
    converter.charset = "UTF-8";
473
 
 
474
 
    let data = converter.convertToByteArray(message, {});
475
 
    hasher.update(data, data.length);
476
 
 
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("");
480
 
  },
481
 
 
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);
487
 
  },
488
 
 
489
 
  /**
490
 
   * Generate a sha256 HMAC for a string message and a given nsIKeyObject
491
 
   */
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);
497
 
  },
498
 
 
499
 
  makeURI: function Weave_makeURI(URIString) {
500
 
    if (!URIString)
501
 
      return null;
502
 
    try {
503
 
      return Svc.IO.newURI(URIString, null, null);
504
 
    } catch (e) {
505
 
      let log = Log4Moz.repository.getLogger("Service.Util");
506
 
      log.debug("Could not create URI: " + Utils.exceptionStr(e));
507
 
      return null;
508
 
    }
509
 
  },
510
 
 
511
 
  makeURL: function Weave_makeURL(URIString) {
512
 
    let url = Utils.makeURI(URIString);
513
 
    url.QueryInterface(Ci.nsIURL);
514
 
    return url;
515
 
  },
516
 
 
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);
521
 
 
522
 
    return xmlDoc.evaluate(xpathString, xmlDoc, nsResolver,
523
 
                           Ci.nsIDOMXPathResult.ANY_TYPE, null);
524
 
  },
525
 
 
526
 
  getTmp: function Weave_getTmp(name) {
527
 
    let tmp = Svc.Directory.get("ProfD", Ci.nsIFile);
528
 
    tmp.QueryInterface(Ci.nsILocalFile);
529
 
 
530
 
    tmp.append("weave");
531
 
    tmp.append("tmp");
532
 
    if (!tmp.exists())
533
 
      tmp.create(tmp.DIRECTORY_TYPE, PERMS_DIRECTORY);
534
 
 
535
 
    if (name)
536
 
      tmp.append(name);
537
 
 
538
 
    return tmp;
539
 
  },
540
 
 
541
 
  /**
542
 
   * Load a json object from disk
543
 
   *
544
 
   * @param filePath
545
 
   *        Json file path load from weave/[filePath].json
546
 
   * @param that
547
 
   *        Object to use for logging and "this" for callback
548
 
   * @param callback
549
 
   *        Function to process json object as its first parameter
550
 
   */
551
 
  jsonLoad: function Utils_jsonLoad(filePath, that, callback) {
552
 
    filePath = "weave/" + filePath + ".json";
553
 
    if (that._log)
554
 
      that._log.trace("Loading json from disk: " + filePath);
555
 
 
556
 
    let file = Utils.getProfileFile(filePath);
557
 
    if (!file.exists())
558
 
      return;
559
 
 
560
 
    try {
561
 
      let [is] = Utils.open(file, "<");
562
 
      let json = Utils.readStream(is);
563
 
      is.close();
564
 
      callback.call(that, JSON.parse(json));
565
 
    }
566
 
    catch (ex) {
567
 
      if (that._log)
568
 
        that._log.debug("Failed to load json: " + Utils.exceptionStr(ex));
569
 
    }
570
 
  },
571
 
 
572
 
  /**
573
 
   * Save a json-able object to disk
574
 
   *
575
 
   * @param filePath
576
 
   *        Json file path save to weave/[filePath].json
577
 
   * @param that
578
 
   *        Object to use for logging and "this" for callback
579
 
   * @param 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.
582
 
   */
583
 
  jsonSave: function Utils_jsonSave(filePath, that, callback) {
584
 
    filePath = "weave/" + filePath + ".json";
585
 
    if (that._log)
586
 
      that._log.trace("Saving json to disk: " + filePath);
587
 
 
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);
593
 
    fos.close();
594
 
  },
595
 
 
596
 
  /**
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.
600
 
   */
601
 
  delay: function delay(callback, wait, thisObj, name) {
602
 
    // Default to running right away
603
 
    wait = wait || 0;
604
 
 
605
 
    // Use a dummy object if one wasn't provided
606
 
    thisObj = thisObj || {};
607
 
 
608
 
    // Delay an existing timer if it exists
609
 
    if (name in thisObj && thisObj[name] instanceof Ci.nsITimer) {
610
 
      thisObj[name].delay = wait;
611
 
      return;
612
 
    }
613
 
 
614
 
    // Create a special timer that we can add extra properties
615
 
    let timer = {};
616
 
    timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
617
 
 
618
 
    // Provide an easy way to clear out the timer
619
 
    timer.clear = function() {
620
 
      thisObj[name] = null;
621
 
      timer.cancel();
622
 
    };
623
 
 
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
628
 
        timer.clear();
629
 
        callback.call(thisObj, timer);
630
 
      }
631
 
    }, wait, timer.TYPE_ONE_SHOT);
632
 
 
633
 
    return thisObj[name] = timer;
634
 
  },
635
 
 
636
 
  open: function open(pathOrFile, mode, perms) {
637
 
    let stream, file;
638
 
 
639
 
    if (pathOrFile instanceof Ci.nsIFile) {
640
 
      file = pathOrFile;
641
 
    } else {
642
 
      file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
643
 
      dump("PATH IS" + pathOrFile + "\n");
644
 
      file.initWithPath(pathOrFile);
645
 
    }
646
 
 
647
 
    if (!perms)
648
 
      perms = PERMS_FILE;
649
 
 
650
 
    switch(mode) {
651
 
    case "<": {
652
 
      if (!file.exists())
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);
661
 
    } break;
662
 
 
663
 
    case ">": {
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);
670
 
    } break;
671
 
 
672
 
    case ">>": {
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);
679
 
    } break;
680
 
 
681
 
    default:
682
 
      throw "Illegal mode to open(): " + mode;
683
 
    }
684
 
 
685
 
    return [stream, file];
686
 
  },
687
 
 
688
 
  /**
689
 
   * Open/reshow a window/dialog based on its name and type.
690
 
   *
691
 
   * @param name
692
 
   *        Name of the window/dialog to reshow if already open
693
 
   * @param type
694
 
   *        Opening behavior: "Window" or "Dialog"
695
 
   * @param args
696
 
   *        More arguments go here depending on the type
697
 
   */
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);
701
 
    if (openedWindow) {
702
 
      openedWindow.focus();
703
 
      return;
704
 
    }
705
 
 
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));
711
 
  },
712
 
 
713
 
  _openChromeWindow: function Utils_openCWindow(name, uri, options, args) {
714
 
    Utils.openWindow(name, "chrome://weave/content/" + uri, options, args);
715
 
  },
716
 
 
717
 
  openWindow: function Utils_openWindow(name, uri, options, args) {
718
 
    Utils._openWin(name, "Window", null, uri, "",
719
 
    options || "centerscreen,chrome,dialog,resizable=yes", args);
720
 
  },
721
 
 
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);
725
 
  },
726
 
 
727
 
  openGenericDialog: function Utils_openGenericDialog(type) {
728
 
    this._genericDialogType = type;
729
 
    this.openDialog("ChangeSomething", "generic-change.xul");
730
 
  },
731
 
 
732
 
  getIcon: function(iconUri, defaultIcon) {
733
 
    try {
734
 
      let iconURI = Utils.makeURI(iconUri);
735
 
      return Svc.Favicon.getFaviconLinkForIcon(iconURI).spec;
736
 
    }
737
 
    catch(ex) {}
738
 
 
739
 
    // Just give the provided default icon or the system's default
740
 
    return defaultIcon || Svc.Favicon.defaultFavicon.spec;
741
 
  },
742
 
 
743
 
  getErrorString: function Utils_getErrorString(error, args) {
744
 
    try {
745
 
      return Str.errors.get(error, args || null);
746
 
    } catch (e) {}
747
 
 
748
 
    // basically returns "Unknown Error"
749
 
    return Str.errors.get("error.reason.unknown");
750
 
  },
751
 
 
752
 
  // assumes an nsIConverterInputStream
753
 
  readStream: function Weave_readStream(is) {
754
 
    let ret = "", str = {};
755
 
    while (is.readString(4096, str) != 0) {
756
 
      ret += str.value;
757
 
    }
758
 
    return ret;
759
 
  },
760
 
 
761
 
  /**
762
 
   * Create an array like the first but without elements of the second
763
 
   */
764
 
  arraySub: function arraySub(minuend, subtrahend) {
765
 
    return minuend.filter(function(i) subtrahend.indexOf(i) == -1);
766
 
  },
767
 
 
768
 
  bind2: function Async_bind2(object, method) {
769
 
    return function innerBind() { return method.apply(object, arguments); };
770
 
  },
771
 
 
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;
778
 
 
779
 
    if (status == slots.SLOT_READY || status == slots.SLOT_LOGGED_IN)
780
 
      return false;
781
 
 
782
 
    if (status == slots.SLOT_NOT_LOGGED_IN)
783
 
      return true;
784
 
    
785
 
    // something wacky happened, pretend MP is locked
786
 
    return true;
787
 
  },
788
 
 
789
 
  __prefs: null,
790
 
  get prefs() {
791
 
    if (!this.__prefs) {
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);
796
 
    }
797
 
    return this.__prefs;
798
 
  }
799
 
};
800
 
 
801
 
let FakeSvc = {
802
 
  // Private Browsing
803
 
  "@mozilla.org/privatebrowsing;1": {
804
 
    autoStarted: false,
805
 
    privateBrowsingEnabled: false
806
 
  },
807
 
  // Session Restore
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;
813
 
    },
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");
818
 
 
819
 
      // Extract various pieces of tab data
820
 
      window.Browser._tabs.forEach(function(tab) {
821
 
        let tabState = { entries: [{}] };
822
 
        let browser = tab.browser;
823
 
 
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)
827
 
          return;
828
 
 
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;
837
 
        }
838
 
        // index is 1-based
839
 
        tabState.index = 1;
840
 
 
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 };
844
 
 
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];
850
 
        }
851
 
 
852
 
        // Add the tab to the window
853
 
        state.windows[0].tabs.push(tabState);
854
 
      });
855
 
      return JSON.stringify(state);
856
 
    }
857
 
  },
858
 
  // A fake service only used for testing
859
 
  "@labs.mozilla.com/Fake/Thing;1": {
860
 
    isFake: true
861
 
  }
862
 
};
863
 
 
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");
868
 
 
869
 
/*
870
 
 * Commonly-used services
871
 
 */
872
 
let Svc = {};
873
 
Svc.Prefs = new Preferences(PREFS_BRANCH);
874
 
Svc.Obs = Observers;
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]));
899
 
 
900
 
let Str = {};
901
 
["engines", "errors", "sync"]
902
 
  .forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy)));