1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5var EXPORTED_SYMBOLS = ["CalStartupService"];
6
7var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
8var { FileSource, L10nRegistry } = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
9
10/**
11 * Helper function to asynchronously call a certain method on the objects passed
12 * in 'services' in order (i.e wait until the first completes before calling the
13 * second
14 *
15 * @param method        The method name to call. Usually startup/shutdown.
16 * @param services      The array of service objects to call on.
17 */
18function callOrderedServices(method, services) {
19  let service = services.shift();
20  if (service) {
21    service[method]({
22      onResult() {
23        callOrderedServices(method, services);
24      },
25    });
26  }
27}
28
29function CalStartupService() {
30  this.wrappedJSObject = this;
31  this.setupObservers();
32}
33
34CalStartupService.prototype = {
35  QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
36  classID: Components.ID("{2547331f-34c0-4a4b-b93c-b503538ba6d6}"),
37
38  // Startup Service Methods
39
40  /**
41   * Sets up the needed observers for noticing startup/shutdown
42   */
43  setupObservers() {
44    Services.obs.addObserver(this, "profile-after-change");
45    Services.obs.addObserver(this, "profile-before-change");
46    Services.obs.addObserver(this, "xpcom-shutdown");
47  },
48
49  started: false,
50
51  /**
52   * Gets the startup order of services. This is an array of service objects
53   * that should be called in order at startup.
54   *
55   * @return      The startup order as an array.
56   */
57  getStartupOrder() {
58    let self = this;
59    let tzService = Cc["@mozilla.org/calendar/timezone-service;1"]
60      .getService(Ci.calITimezoneService)
61      .QueryInterface(Ci.calIStartupService);
62    let calMgr = Cc["@mozilla.org/calendar/manager;1"]
63      .getService(Ci.calICalendarManager)
64      .QueryInterface(Ci.calIStartupService);
65
66    // Localization service
67    let locales = {
68      startup(aCompleteListener) {
69        let packaged = Services.locale.packagedLocales;
70        let fileSrc = new FileSource(
71          "calendar",
72          packaged,
73          "resource:///chrome/{locale}/locale/{locale}/calendar/"
74        );
75        L10nRegistry.registerSources([fileSrc]);
76        aCompleteListener.onResult(null, Cr.NS_OK);
77      },
78      shutdown(aCompleteListener) {
79        aCompleteListener.onResult(null, Cr.NS_OK);
80      },
81    };
82
83    // Notification object
84    let notify = {
85      startup(aCompleteListener) {
86        self.started = true;
87        Services.obs.notifyObservers(null, "calendar-startup-done");
88        aCompleteListener.onResult(null, Cr.NS_OK);
89      },
90      shutdown(aCompleteListener) {
91        // Argh, it would have all been so pretty! Since we just reverse
92        // the array, the shutdown notification would happen before the
93        // other shutdown calls. For lack of pretty code, I'm
94        // leaving this out! Users can still listen to xpcom-shutdown.
95        self.started = false;
96        aCompleteListener.onResult(null, Cr.NS_OK);
97      },
98    };
99
100    // We need to spin up the timezone service before the calendar manager
101    // to ensure we have the timezones initialized. Make sure "notify" is
102    // last in this array!
103    return [locales, tzService, calMgr, notify];
104  },
105
106  /**
107   * Observer notification callback
108   */
109  observe(aSubject, aTopic, aData) {
110    switch (aTopic) {
111      case "profile-after-change":
112        callOrderedServices("startup", this.getStartupOrder());
113        break;
114      case "profile-before-change":
115        callOrderedServices("shutdown", this.getStartupOrder().reverse());
116        break;
117      case "xpcom-shutdown":
118        Services.obs.removeObserver(this, "profile-after-change");
119        Services.obs.removeObserver(this, "profile-before-change");
120        Services.obs.removeObserver(this, "xpcom-shutdown");
121        break;
122    }
123  },
124};
125