Backed out changeset 12313cce9281 (bug 1083449) for suspicion of causing B2G debug...
[gecko.git] / dom / network / NetworkStatsManager.js
bloba2ce59a910b1ef086fed57d4a46303b124a5a306
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/. */
5 "use strict";
7 const DEBUG = false;
8 function debug(s) { dump("-*- NetworkStatsManager: " + s + "\n"); }
10 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
13 Cu.import("resource://gre/modules/Services.jsm");
14 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
16 // Ensure NetworkStatsService and NetworkStatsDB are loaded in the parent process
17 // to receive messages from the child processes.
18 let appInfo = Cc["@mozilla.org/xre/app-info;1"];
19 let isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
20                         .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
21 if (isParentProcess) {
22   Cu.import("resource://gre/modules/NetworkStatsService.jsm");
25 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
26                                    "@mozilla.org/childprocessmessagemanager;1",
27                                    "nsISyncMessageSender");
29 // NetworkStatsData
30 const nsIClassInfo         = Ci.nsIClassInfo;
31 const NETWORKSTATSDATA_CID = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}");
33 function NetworkStatsData(aWindow, aData) {
34   this.rxBytes = aData.rxBytes;
35   this.txBytes = aData.txBytes;
36   this.date = new aWindow.Date(aData.date.getTime());
39 NetworkStatsData.prototype = {
40   classID : NETWORKSTATSDATA_CID,
42   QueryInterface : XPCOMUtils.generateQI([])
45 // NetworkStatsInterface
46 const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
47 const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
49 function NetworkStatsInterface() {
50   if (DEBUG) {
51     debug("NetworkStatsInterface Constructor");
52   }
55 NetworkStatsInterface.prototype = {
56   __init: function(aNetwork) {
57     this.type = aNetwork.type;
58     this.id = aNetwork.id;
59   },
61   classID : NETWORKSTATSINTERFACE_CID,
63   contractID: NETWORKSTATSINTERFACE_CONTRACTID,
64   QueryInterface : XPCOMUtils.generateQI([])
67 // NetworkStats
68 const NETWORKSTATS_CID = Components.ID("{28904f59-8497-4ac0-904f-2af14b7fd3de}");
70 function NetworkStats(aWindow, aStats) {
71   if (DEBUG) {
72     debug("NetworkStats Constructor");
73   }
74   this.appManifestURL = aStats.appManifestURL || null;
75   this.serviceType = aStats.serviceType || null;
76   this.network = new aWindow.MozNetworkStatsInterface(aStats.network);
77   this.start = aStats.start ? new aWindow.Date(aStats.start.getTime()) : null;
78   this.end = aStats.end ? new aWindow.Date(aStats.end.getTime()) : null;
80   let samples = this.data = new aWindow.Array();
81   for (let i = 0; i < aStats.data.length; i++) {
82     samples.push(aWindow.MozNetworkStatsData._create(
83       aWindow, new NetworkStatsData(aWindow, aStats.data[i])));
84   }
87 NetworkStats.prototype = {
88   classID : NETWORKSTATS_CID,
90   QueryInterface : XPCOMUtils.generateQI()
93 // NetworkStatsAlarm
94 const NETWORKSTATSALARM_CID = Components.ID("{a93ea13e-409c-4189-9b1e-95fff220be55}");
96 function NetworkStatsAlarm(aWindow, aAlarm) {
97   this.alarmId = aAlarm.id;
98   this.network = new aWindow.MozNetworkStatsInterface(aAlarm.network);
99   this.threshold = aAlarm.threshold;
100   this.data = aAlarm.data;
103 NetworkStatsAlarm.prototype = {
104   classID : NETWORKSTATSALARM_CID,
106   QueryInterface : XPCOMUtils.generateQI([])
109 // NetworkStatsManager
111 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
112 const NETWORKSTATSMANAGER_CID        = Components.ID("{ceb874cd-cc1a-4e65-b404-cc2d3e42425f}");
113 const nsIDOMMozNetworkStatsManager   = Ci.nsIDOMMozNetworkStatsManager;
115 function NetworkStatsManager() {
116   if (DEBUG) {
117     debug("Constructor");
118   }
121 NetworkStatsManager.prototype = {
122   __proto__: DOMRequestIpcHelper.prototype,
124   checkPrivileges: function checkPrivileges() {
125     if (!this.hasPrivileges) {
126       throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE);
127     }
128   },
130   getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) {
131     this.checkPrivileges();
133     if (aStart.constructor.name !== "Date" ||
134         aEnd.constructor.name !== "Date" ||
135         !(aNetwork instanceof this.window.MozNetworkStatsInterface) ||
136         aStart > aEnd) {
137       throw Components.results.NS_ERROR_INVALID_ARG;
138     }
140     let appManifestURL = null;
141     let serviceType = null;
142     if (aOptions) {
143       if (aOptions.appManifestURL && aOptions.serviceType) {
144         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
145       }
146       appManifestURL = aOptions.appManifestURL;
147       serviceType = aOptions.serviceType;
148     }
150     // TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC
151     // This is just a work-around by passing timestamp numbers.
152     aStart = aStart.getTime();
153     aEnd = aEnd.getTime();
155     let request = this.createRequest();
156     cpmm.sendAsyncMessage("NetworkStats:Get",
157                           { network: aNetwork.toJSON(),
158                             start: aStart,
159                             end: aEnd,
160                             appManifestURL: appManifestURL,
161                             serviceType: serviceType,
162                             id: this.getRequestId(request) });
163     return request;
164   },
166   clearStats: function clearStats(aNetwork) {
167     this.checkPrivileges();
169     if (!aNetwork instanceof this.window.MozNetworkStatsInterface) {
170       throw Components.results.NS_ERROR_INVALID_ARG;
171     }
173     let request = this.createRequest();
174     cpmm.sendAsyncMessage("NetworkStats:Clear",
175                           { network: aNetwork.toJSON(),
176                             id: this.getRequestId(request) });
177     return request;
178   },
180   clearAllStats: function clearAllStats() {
181     this.checkPrivileges();
183     let request = this.createRequest();
184     cpmm.sendAsyncMessage("NetworkStats:ClearAll",
185                           {id: this.getRequestId(request)});
186     return request;
187   },
189   addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
190     this.checkPrivileges();
192     if (!aOptions) {
193       aOptions = Object.create(null);
194     }
196     if (aOptions.startTime && aOptions.startTime.constructor.name !== "Date" ||
197         !(aNetwork instanceof this.window.MozNetworkStatsInterface)) {
198       throw Components.results.NS_ERROR_INVALID_ARG;
199     }
201     let request = this.createRequest();
202     cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
203                           {id: this.getRequestId(request),
204                            data: {network: aNetwork.toJSON(),
205                                   threshold: aThreshold,
206                                   startTime: aOptions.startTime,
207                                   data: aOptions.data,
208                                   manifestURL: this.manifestURL,
209                                   pageURL: this.pageURL}});
210     return request;
211   },
213   getAllAlarms: function getAllAlarms(aNetwork) {
214     this.checkPrivileges();
216     let network = null;
217     if (aNetwork) {
218       if (!aNetwork instanceof this.window.MozNetworkStatsInterface) {
219         throw Components.results.NS_ERROR_INVALID_ARG;
220       }
221       network = aNetwork.toJSON();
222     }
224     let request = this.createRequest();
225     cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
226                           {id: this.getRequestId(request),
227                            data: {network: network,
228                                   manifestURL: this.manifestURL}});
229     return request;
230   },
232   removeAlarms: function removeAlarms(aAlarmId) {
233     this.checkPrivileges();
235     if (aAlarmId == 0) {
236       aAlarmId = -1;
237     }
239     let request = this.createRequest();
240     cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
241                           {id: this.getRequestId(request),
242                            data: {alarmId: aAlarmId,
243                                   manifestURL: this.manifestURL}});
245     return request;
246   },
248   getAvailableNetworks: function getAvailableNetworks() {
249     this.checkPrivileges();
251     let request = this.createRequest();
252     cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks",
253                           { id: this.getRequestId(request) });
254     return request;
255   },
257   getAvailableServiceTypes: function getAvailableServiceTypes() {
258     this.checkPrivileges();
260     let request = this.createRequest();
261     cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes",
262                           { id: this.getRequestId(request) });
263     return request;
264   },
266   get sampleRate() {
267     this.checkPrivileges();
268     return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
269   },
271   get maxStorageAge() {
272     this.checkPrivileges();
273     return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
274   },
276   receiveMessage: function(aMessage) {
277     if (DEBUG) {
278       debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
279     }
281     let msg = aMessage.json;
282     let req = this.takeRequest(msg.id);
283     if (!req) {
284       if (DEBUG) {
285         debug("No request stored with id " + msg.id);
286       }
287       return;
288     }
290     switch (aMessage.name) {
291       case "NetworkStats:Get:Return":
292         if (msg.error) {
293           Services.DOMRequest.fireError(req, msg.error);
294           return;
295         }
297         let result = this._window.MozNetworkStats._create(
298           this._window, new NetworkStats(this._window, msg.result));
299         if (DEBUG) {
300           debug("result: " + JSON.stringify(result));
301         }
302         Services.DOMRequest.fireSuccess(req, result);
303         break;
305       case "NetworkStats:GetAvailableNetworks:Return":
306         if (msg.error) {
307           Services.DOMRequest.fireError(req, msg.error);
308           return;
309         }
311         let networks = new this._window.Array();
312         for (let i = 0; i < msg.result.length; i++) {
313           let network = new this._window.MozNetworkStatsInterface(msg.result[i]);
314           networks.push(network);
315         }
317         Services.DOMRequest.fireSuccess(req, networks);
318         break;
320       case "NetworkStats:GetAvailableServiceTypes:Return":
321         if (msg.error) {
322           Services.DOMRequest.fireError(req, msg.error);
323           return;
324         }
326         let serviceTypes = new this._window.Array();
327         for (let i = 0; i < msg.result.length; i++) {
328           serviceTypes.push(msg.result[i]);
329         }
331         Services.DOMRequest.fireSuccess(req, serviceTypes);
332         break;
334       case "NetworkStats:Clear:Return":
335       case "NetworkStats:ClearAll:Return":
336         if (msg.error) {
337           Services.DOMRequest.fireError(req, msg.error);
338           return;
339         }
341         Services.DOMRequest.fireSuccess(req, true);
342         break;
344       case "NetworkStats:SetAlarm:Return":
345       case "NetworkStats:RemoveAlarms:Return":
346         if (msg.error) {
347           Services.DOMRequest.fireError(req, msg.error);
348           return;
349         }
351         Services.DOMRequest.fireSuccess(req, msg.result);
352         break;
354       case "NetworkStats:GetAlarms:Return":
355         if (msg.error) {
356           Services.DOMRequest.fireError(req, msg.error);
357           return;
358         }
360         let alarms = new this._window.Array();
361         for (let i = 0; i < msg.result.length; i++) {
362           // The WebIDL type of data is any, so we should manually clone it
363           // into the content window.
364           if ("data" in msg.result[i]) {
365             msg.result[i].data = Cu.cloneInto(msg.result[i].data, this._window);
366           }
367           let alarm = new NetworkStatsAlarm(this._window, msg.result[i]);
368           alarms.push(this._window.MozNetworkStatsAlarm._create(this._window, alarm));
369         }
371         Services.DOMRequest.fireSuccess(req, alarms);
372         break;
374       default:
375         if (DEBUG) {
376           debug("Wrong message: " + aMessage.name);
377         }
378     }
379   },
381   init: function(aWindow) {
382     // Set navigator.mozNetworkStats to null.
383     if (!Services.prefs.getBoolPref("dom.mozNetworkStats.enabled")) {
384       return null;
385     }
387     let principal = aWindow.document.nodePrincipal;
388     let secMan = Services.scriptSecurityManager;
389     let perm = principal == secMan.getSystemPrincipal() ?
390                  Ci.nsIPermissionManager.ALLOW_ACTION :
391                  Services.perms.testExactPermissionFromPrincipal(principal,
392                                                                  "networkstats-manage");
394     // Only pages with perm set can use the netstats.
395     this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
396     if (DEBUG) {
397       debug("has privileges: " + this.hasPrivileges);
398     }
400     if (!this.hasPrivileges) {
401       return null;
402     }
404     this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
405                                         "NetworkStats:GetAvailableNetworks:Return",
406                                         "NetworkStats:GetAvailableServiceTypes:Return",
407                                         "NetworkStats:Clear:Return",
408                                         "NetworkStats:ClearAll:Return",
409                                         "NetworkStats:SetAlarm:Return",
410                                         "NetworkStats:GetAlarms:Return",
411                                         "NetworkStats:RemoveAlarms:Return"]);
413     // Init app properties.
414     let appsService = Cc["@mozilla.org/AppsService;1"]
415                         .getService(Ci.nsIAppsService);
417     this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
419     let isApp = !!this.manifestURL.length;
420     if (isApp) {
421       this.pageURL = principal.URI.spec;
422     }
424     this.window = aWindow;
425   },
427   // Called from DOMRequestIpcHelper
428   uninit: function uninit() {
429     if (DEBUG) {
430       debug("uninit call");
431     }
432   },
434   classID : NETWORKSTATSMANAGER_CID,
435   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager,
436                                          Ci.nsIDOMGlobalPropertyInitializer,
437                                          Ci.nsISupportsWeakReference,
438                                          Ci.nsIObserver]),
440   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
441                                      contractID: NETWORKSTATSMANAGER_CONTRACTID,
442                                      classDescription: "NetworkStatsManager",
443                                      interfaces: [nsIDOMMozNetworkStatsManager],
444                                      flags: nsIClassInfo.DOM_OBJECT})
447 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
448                                                      NetworkStatsData,
449                                                      NetworkStatsInterface,
450                                                      NetworkStats,
451                                                      NetworkStatsManager]);