Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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");
23 }
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}");
32 const nsIDOMMozNetworkStatsData = Ci.nsIDOMMozNetworkStatsData;
34 function NetworkStatsData(aWindow, aData) {
35 this.rxBytes = aData.rxBytes;
36 this.txBytes = aData.txBytes;
37 this.date = new aWindow.Date(aData.date.getTime());
38 }
40 NetworkStatsData.prototype = {
41 __exposedProps__: {
42 rxBytes: 'r',
43 txBytes: 'r',
44 date: 'r',
45 },
47 classID : NETWORKSTATSDATA_CID,
48 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSDATA_CID,
49 contractID:"@mozilla.org/networkstatsdata;1",
50 classDescription: "NetworkStatsData",
51 interfaces: [nsIDOMMozNetworkStatsData],
52 flags: nsIClassInfo.DOM_OBJECT}),
54 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData])
55 };
57 // NetworkStatsInterface
58 const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
59 const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
60 const nsIDOMMozNetworkStatsInterface = Ci.nsIDOMMozNetworkStatsInterface;
62 function NetworkStatsInterface(aNetwork) {
63 if (DEBUG) {
64 debug("NetworkStatsInterface Constructor");
65 }
66 this.type = aNetwork.type;
67 this.id = aNetwork.id;
68 }
70 NetworkStatsInterface.prototype = {
71 __exposedProps__: {
72 id: 'r',
73 type: 'r',
74 },
76 classID : NETWORKSTATSINTERFACE_CID,
77 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
78 contractID: NETWORKSTATSINTERFACE_CONTRACTID,
79 classDescription: "NetworkStatsInterface",
80 interfaces: [nsIDOMMozNetworkStatsInterface],
81 flags: nsIClassInfo.DOM_OBJECT}),
83 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface])
84 }
86 // NetworkStats
87 const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
88 const NETWORKSTATS_CID = Components.ID("{f1996e44-1057-4d4b-8ff8-919e76c4cfa9}");
89 const nsIDOMMozNetworkStats = Ci.nsIDOMMozNetworkStats;
91 function NetworkStats(aWindow, aStats) {
92 if (DEBUG) {
93 debug("NetworkStats Constructor");
94 }
95 this.appManifestURL = aStats.appManifestURL || null;
96 this.serviceType = aStats.serviceType || null;
97 this.network = new NetworkStatsInterface(aStats.network);
98 this.start = aStats.start ? new aWindow.Date(aStats.start.getTime()) : null;
99 this.end = aStats.end ? new aWindow.Date(aStats.end.getTime()) : null;
101 let samples = this.data = new aWindow.Array();
102 for (let i = 0; i < aStats.data.length; i++) {
103 samples.push(new NetworkStatsData(aWindow, aStats.data[i]));
104 }
105 }
107 NetworkStats.prototype = {
108 __exposedProps__: {
109 appManifestURL: 'r',
110 serviceType: 'r',
111 network: 'r',
112 start: 'r',
113 end: 'r',
114 data: 'r',
115 },
117 classID : NETWORKSTATS_CID,
118 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID,
119 contractID: NETWORKSTATS_CONTRACTID,
120 classDescription: "NetworkStats",
121 interfaces: [nsIDOMMozNetworkStats],
122 flags: nsIClassInfo.DOM_OBJECT}),
124 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
125 nsIDOMMozNetworkStatsData,
126 nsIDOMMozNetworkStatsInterface])
127 }
129 // NetworkStatsAlarm
130 const NETWORKSTATSALARM_CID = Components.ID("{063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}");
131 const nsIDOMMozNetworkStatsAlarm = Ci.nsIDOMMozNetworkStatsAlarm;
133 function NetworkStatsAlarm(aAlarm) {
134 this.alarmId = aAlarm.id;
135 this.network = new NetworkStatsInterface(aAlarm.network);
136 this.threshold = aAlarm.threshold;
137 this.data = aAlarm.data;
138 }
140 NetworkStatsAlarm.prototype = {
141 __exposedProps__: {
142 alarmId: 'r',
143 network: 'r',
144 threshold: 'r',
145 data: 'r',
146 },
148 classID : NETWORKSTATSALARM_CID,
149 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSALARM_CID,
150 contractID:"@mozilla.org/networkstatsalarm;1",
151 classDescription: "NetworkStatsAlarm",
152 interfaces: [nsIDOMMozNetworkStatsAlarm],
153 flags: nsIClassInfo.DOM_OBJECT}),
155 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsAlarm])
156 };
158 // NetworkStatsManager
160 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
161 const NETWORKSTATSMANAGER_CID = Components.ID("{8a66f4c1-0c25-4a66-9fc5-0106947b91f9}");
162 const nsIDOMMozNetworkStatsManager = Ci.nsIDOMMozNetworkStatsManager;
164 function NetworkStatsManager() {
165 if (DEBUG) {
166 debug("Constructor");
167 }
168 }
170 NetworkStatsManager.prototype = {
171 __proto__: DOMRequestIpcHelper.prototype,
173 checkPrivileges: function checkPrivileges() {
174 if (!this.hasPrivileges) {
175 throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE);
176 }
177 },
179 getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) {
180 this.checkPrivileges();
182 if (aStart.constructor.name !== "Date" ||
183 aEnd.constructor.name !== "Date" ||
184 aStart > aEnd) {
185 throw Components.results.NS_ERROR_INVALID_ARG;
186 }
188 let appManifestURL = null;
189 let serviceType = null;
190 if (aOptions) {
191 if (aOptions.appManifestURL && aOptions.serviceType) {
192 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
193 }
194 appManifestURL = aOptions.appManifestURL;
195 serviceType = aOptions.serviceType;
196 }
198 // TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC
199 // This is just a work-around by passing timestamp numbers.
200 aStart = aStart.getTime();
201 aEnd = aEnd.getTime();
203 let request = this.createRequest();
204 cpmm.sendAsyncMessage("NetworkStats:Get",
205 { network: aNetwork,
206 start: aStart,
207 end: aEnd,
208 appManifestURL: appManifestURL,
209 serviceType: serviceType,
210 id: this.getRequestId(request) });
211 return request;
212 },
214 clearStats: function clearStats(aNetwork) {
215 this.checkPrivileges();
217 let request = this.createRequest();
218 cpmm.sendAsyncMessage("NetworkStats:Clear",
219 { network: aNetwork,
220 id: this.getRequestId(request) });
221 return request;
222 },
224 clearAllStats: function clearAllStats() {
225 this.checkPrivileges();
227 let request = this.createRequest();
228 cpmm.sendAsyncMessage("NetworkStats:ClearAll",
229 {id: this.getRequestId(request)});
230 return request;
231 },
233 addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
234 this.checkPrivileges();
236 if (!aOptions) {
237 aOptions = Object.create(null);
238 }
240 if (aOptions.startTime && aOptions.startTime.constructor.name !== "Date") {
241 throw Components.results.NS_ERROR_INVALID_ARG;
242 }
244 let request = this.createRequest();
245 cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
246 {id: this.getRequestId(request),
247 data: {network: aNetwork,
248 threshold: aThreshold,
249 startTime: aOptions.startTime,
250 data: aOptions.data,
251 manifestURL: this.manifestURL,
252 pageURL: this.pageURL}});
253 return request;
254 },
256 getAllAlarms: function getAllAlarms(aNetwork) {
257 this.checkPrivileges();
259 let request = this.createRequest();
260 cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
261 {id: this.getRequestId(request),
262 data: {network: aNetwork,
263 manifestURL: this.manifestURL}});
264 return request;
265 },
267 removeAlarms: function removeAlarms(aAlarmId) {
268 this.checkPrivileges();
270 if (aAlarmId == 0) {
271 aAlarmId = -1;
272 }
274 let request = this.createRequest();
275 cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
276 {id: this.getRequestId(request),
277 data: {alarmId: aAlarmId,
278 manifestURL: this.manifestURL}});
280 return request;
281 },
283 getAvailableNetworks: function getAvailableNetworks() {
284 this.checkPrivileges();
286 let request = this.createRequest();
287 cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks",
288 { id: this.getRequestId(request) });
289 return request;
290 },
292 getAvailableServiceTypes: function getAvailableServiceTypes() {
293 this.checkPrivileges();
295 let request = this.createRequest();
296 cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes",
297 { id: this.getRequestId(request) });
298 return request;
299 },
301 get sampleRate() {
302 this.checkPrivileges();
303 return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
304 },
306 get maxStorageAge() {
307 this.checkPrivileges();
308 return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
309 },
311 receiveMessage: function(aMessage) {
312 if (DEBUG) {
313 debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
314 }
316 let msg = aMessage.json;
317 let req = this.takeRequest(msg.id);
318 if (!req) {
319 if (DEBUG) {
320 debug("No request stored with id " + msg.id);
321 }
322 return;
323 }
325 switch (aMessage.name) {
326 case "NetworkStats:Get:Return":
327 if (msg.error) {
328 Services.DOMRequest.fireError(req, msg.error);
329 return;
330 }
332 let result = new NetworkStats(this._window, msg.result);
333 if (DEBUG) {
334 debug("result: " + JSON.stringify(result));
335 }
336 Services.DOMRequest.fireSuccess(req, result);
337 break;
339 case "NetworkStats:GetAvailableNetworks:Return":
340 if (msg.error) {
341 Services.DOMRequest.fireError(req, msg.error);
342 return;
343 }
345 let networks = new this._window.Array();
346 for (let i = 0; i < msg.result.length; i++) {
347 networks.push(new NetworkStatsInterface(msg.result[i]));
348 }
350 Services.DOMRequest.fireSuccess(req, networks);
351 break;
353 case "NetworkStats:GetAvailableServiceTypes:Return":
354 if (msg.error) {
355 Services.DOMRequest.fireError(req, msg.error);
356 return;
357 }
359 let serviceTypes = new this._window.Array();
360 for (let i = 0; i < msg.result.length; i++) {
361 serviceTypes.push(msg.result[i]);
362 }
364 Services.DOMRequest.fireSuccess(req, serviceTypes);
365 break;
367 case "NetworkStats:Clear:Return":
368 case "NetworkStats:ClearAll:Return":
369 if (msg.error) {
370 Services.DOMRequest.fireError(req, msg.error);
371 return;
372 }
374 Services.DOMRequest.fireSuccess(req, true);
375 break;
377 case "NetworkStats:SetAlarm:Return":
378 case "NetworkStats:RemoveAlarms:Return":
379 if (msg.error) {
380 Services.DOMRequest.fireError(req, msg.error);
381 return;
382 }
384 Services.DOMRequest.fireSuccess(req, msg.result);
385 break;
387 case "NetworkStats:GetAlarms:Return":
388 if (msg.error) {
389 Services.DOMRequest.fireError(req, msg.error);
390 return;
391 }
393 let alarms = new this._window.Array();
394 for (let i = 0; i < msg.result.length; i++) {
395 alarms.push(new NetworkStatsAlarm(msg.result[i]));
396 }
398 Services.DOMRequest.fireSuccess(req, alarms);
399 break;
401 default:
402 if (DEBUG) {
403 debug("Wrong message: " + aMessage.name);
404 }
405 }
406 },
408 init: function(aWindow) {
409 // Set navigator.mozNetworkStats to null.
410 if (!Services.prefs.getBoolPref("dom.mozNetworkStats.enabled")) {
411 return null;
412 }
414 let principal = aWindow.document.nodePrincipal;
415 let secMan = Services.scriptSecurityManager;
416 let perm = principal == secMan.getSystemPrincipal() ?
417 Ci.nsIPermissionManager.ALLOW_ACTION :
418 Services.perms.testExactPermissionFromPrincipal(principal,
419 "networkstats-manage");
421 // Only pages with perm set can use the netstats.
422 this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
423 if (DEBUG) {
424 debug("has privileges: " + this.hasPrivileges);
425 }
427 if (!this.hasPrivileges) {
428 return null;
429 }
431 this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
432 "NetworkStats:GetAvailableNetworks:Return",
433 "NetworkStats:GetAvailableServiceTypes:Return",
434 "NetworkStats:Clear:Return",
435 "NetworkStats:ClearAll:Return",
436 "NetworkStats:SetAlarm:Return",
437 "NetworkStats:GetAlarms:Return",
438 "NetworkStats:RemoveAlarms:Return"]);
440 // Init app properties.
441 let appsService = Cc["@mozilla.org/AppsService;1"]
442 .getService(Ci.nsIAppsService);
444 this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
446 let isApp = !!this.manifestURL.length;
447 if (isApp) {
448 this.pageURL = principal.URI.spec;
449 }
450 },
452 // Called from DOMRequestIpcHelper
453 uninit: function uninit() {
454 if (DEBUG) {
455 debug("uninit call");
456 }
457 },
459 classID : NETWORKSTATSMANAGER_CID,
460 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager,
461 Ci.nsIDOMGlobalPropertyInitializer,
462 Ci.nsISupportsWeakReference,
463 Ci.nsIObserver]),
465 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
466 contractID: NETWORKSTATSMANAGER_CONTRACTID,
467 classDescription: "NetworkStatsManager",
468 interfaces: [nsIDOMMozNetworkStatsManager],
469 flags: nsIClassInfo.DOM_OBJECT})
470 }
472 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
473 NetworkStatsData,
474 NetworkStatsInterface,
475 NetworkStats,
476 NetworkStatsManager]);