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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
9 const REQUEST_CPU_LOCK_TIMEOUT = 10 * 1000; // 10 seconds.
11 function debug(aStr) {
13 dump("AlarmsManager: " + aStr + "\n");
16 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
18 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
19 Cu.import("resource://gre/modules/Services.jsm");
20 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
22 XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
23 "@mozilla.org/power/powermanagerservice;1",
24 "nsIPowerManagerService");
26 function AlarmsManager() {
29 // A <requestId, {cpuLock, timer}> map.
30 this._cpuLockDict = new Map();
33 AlarmsManager.prototype = {
34 __proto__: DOMRequestIpcHelper.prototype,
36 contractID : "@mozilla.org/alarmsManager;1",
38 classID : Components.ID("{fea1e884-9b05-11e1-9b64-87a7016c3860}"),
40 QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
41 Ci.nsISupportsWeakReference,
44 add: function add(aDate, aRespectTimezone, aData) {
47 if (!this._manifestURL) {
48 debug("Cannot add alarms for non-installed apps.");
49 throw Components.results.NS_ERROR_FAILURE;
53 throw Components.results.NS_ERROR_INVALID_ARG;
56 let isIgnoreTimezone = true;
58 switch (aRespectTimezone) {
60 isIgnoreTimezone = false;
63 case "ignoreTimezone":
64 isIgnoreTimezone = true;
68 throw Components.results.NS_ERROR_INVALID_ARG;
74 // Run JSON.stringify() in the sand box with the principal of the calling
75 // web page to ensure no cross-origin object is involved. A "Permission
76 // Denied" error will be thrown in case of privilege violation.
77 let sandbox = new Cu.Sandbox(Cu.getWebIDLCallerPrincipal());
79 data = JSON.parse(Cu.evalInSandbox("JSON.stringify(data)", sandbox));
81 let request = this.createRequest();
82 let requestId = this.getRequestId(request);
83 this._lockCpuForRequest(requestId);
84 this._cpmm.sendAsyncMessage("AlarmsManager:Add",
85 { requestId: requestId,
87 ignoreTimezone: isIgnoreTimezone,
89 pageURL: this._pageURL,
90 manifestURL: this._manifestURL });
94 remove: function remove(aId) {
97 this._cpmm.sendAsyncMessage("AlarmsManager:Remove",
98 { id: aId, manifestURL: this._manifestURL });
101 getAll: function getAll() {
104 let request = this.createRequest();
105 this._cpmm.sendAsyncMessage("AlarmsManager:GetAll",
106 { requestId: this.getRequestId(request),
107 manifestURL: this._manifestURL });
111 receiveMessage: function receiveMessage(aMessage) {
112 debug("receiveMessage(): " + aMessage.name);
114 let json = aMessage.json;
115 let request = this.getRequest(json.requestId);
118 debug("No request stored! " + json.requestId);
122 switch (aMessage.name) {
123 case "AlarmsManager:Add:Return:OK":
124 this._unlockCpuForRequest(json.requestId);
125 Services.DOMRequest.fireSuccess(request, json.id);
128 case "AlarmsManager:GetAll:Return:OK":
129 // We don't need to expose everything to the web content.
131 json.alarms.forEach(function trimAlarmInfo(aAlarm) {
132 let alarm = { "id": aAlarm.id,
134 "respectTimezone": aAlarm.ignoreTimezone ?
135 "ignoreTimezone" : "honorTimezone",
136 "data": aAlarm.data };
140 Services.DOMRequest.fireSuccess(request,
141 Cu.cloneInto(alarms, this._window));
144 case "AlarmsManager:Add:Return:KO":
145 this._unlockCpuForRequest(json.requestId);
146 Services.DOMRequest.fireError(request, json.errorMsg);
149 case "AlarmsManager:GetAll:Return:KO":
150 Services.DOMRequest.fireError(request, json.errorMsg);
154 debug("Wrong message: " + aMessage.name);
158 this.removeRequest(json.requestId);
161 // nsIDOMGlobalPropertyInitializer implementation
162 init: function init(aWindow) {
165 this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
166 .getService(Ci.nsISyncMessageSender);
168 // Add the valid messages to be listened.
169 this.initDOMRequestHelper(aWindow, ["AlarmsManager:Add:Return:OK",
170 "AlarmsManager:Add:Return:KO",
171 "AlarmsManager:GetAll:Return:OK",
172 "AlarmsManager:GetAll:Return:KO"]);
174 // Get the manifest URL if this is an installed app
175 let appsService = Cc["@mozilla.org/AppsService;1"]
176 .getService(Ci.nsIAppsService);
177 let principal = aWindow.document.nodePrincipal;
178 this._pageURL = principal.URI.spec;
179 this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
180 this._window = aWindow;
183 // Called from DOMRequestIpcHelper.
184 uninit: function uninit() {
188 _lockCpuForRequest: function (aRequestId) {
189 if (this._cpuLockDict.has(aRequestId)) {
190 debug('Cpu wakelock for request ' + aRequestId + ' has been acquired. ' +
191 'You may call this function repeatedly or requestId is collision.');
195 // Acquire a lock for given request and save for lookup lately.
196 debug('Acquire cpu lock for request ' + aRequestId);
198 cpuLock: gPowerManagerService.newWakeLock("cpu"),
199 timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
201 this._cpuLockDict.set(aRequestId, cpuLockInfo);
203 // Start a timer to prevent from non-responding request.
204 cpuLockInfo.timer.initWithCallback(() => {
205 debug('Request timeout! Release the cpu lock');
206 this._unlockCpuForRequest(aRequestId);
207 }, REQUEST_CPU_LOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
210 _unlockCpuForRequest: function(aRequestId) {
211 let cpuLockInfo = this._cpuLockDict.get(aRequestId);
213 debug('The cpu lock for requestId ' + aRequestId + ' is either invalid ' +
214 'or has been released.');
218 // Release the cpu lock and cancel the timer.
219 debug('Release the cpu lock for ' + aRequestId);
220 cpuLockInfo.cpuLock.unlock();
221 cpuLockInfo.timer.cancel();
222 this._cpuLockDict.delete(aRequestId);
227 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlarmsManager])