|
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/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const DEBUG = false; |
|
8 function debug(s) { dump("-*- NetworkStatsManager: " + s + "\n"); } |
|
9 |
|
10 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
11 |
|
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
13 Cu.import("resource://gre/modules/Services.jsm"); |
|
14 Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); |
|
15 |
|
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 } |
|
24 |
|
25 XPCOMUtils.defineLazyServiceGetter(this, "cpmm", |
|
26 "@mozilla.org/childprocessmessagemanager;1", |
|
27 "nsISyncMessageSender"); |
|
28 |
|
29 // NetworkStatsData |
|
30 const nsIClassInfo = Ci.nsIClassInfo; |
|
31 const NETWORKSTATSDATA_CID = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}"); |
|
32 const nsIDOMMozNetworkStatsData = Ci.nsIDOMMozNetworkStatsData; |
|
33 |
|
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 } |
|
39 |
|
40 NetworkStatsData.prototype = { |
|
41 __exposedProps__: { |
|
42 rxBytes: 'r', |
|
43 txBytes: 'r', |
|
44 date: 'r', |
|
45 }, |
|
46 |
|
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}), |
|
53 |
|
54 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData]) |
|
55 }; |
|
56 |
|
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; |
|
61 |
|
62 function NetworkStatsInterface(aNetwork) { |
|
63 if (DEBUG) { |
|
64 debug("NetworkStatsInterface Constructor"); |
|
65 } |
|
66 this.type = aNetwork.type; |
|
67 this.id = aNetwork.id; |
|
68 } |
|
69 |
|
70 NetworkStatsInterface.prototype = { |
|
71 __exposedProps__: { |
|
72 id: 'r', |
|
73 type: 'r', |
|
74 }, |
|
75 |
|
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}), |
|
82 |
|
83 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface]) |
|
84 } |
|
85 |
|
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; |
|
90 |
|
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; |
|
100 |
|
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 } |
|
106 |
|
107 NetworkStats.prototype = { |
|
108 __exposedProps__: { |
|
109 appManifestURL: 'r', |
|
110 serviceType: 'r', |
|
111 network: 'r', |
|
112 start: 'r', |
|
113 end: 'r', |
|
114 data: 'r', |
|
115 }, |
|
116 |
|
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}), |
|
123 |
|
124 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats, |
|
125 nsIDOMMozNetworkStatsData, |
|
126 nsIDOMMozNetworkStatsInterface]) |
|
127 } |
|
128 |
|
129 // NetworkStatsAlarm |
|
130 const NETWORKSTATSALARM_CID = Components.ID("{063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}"); |
|
131 const nsIDOMMozNetworkStatsAlarm = Ci.nsIDOMMozNetworkStatsAlarm; |
|
132 |
|
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 } |
|
139 |
|
140 NetworkStatsAlarm.prototype = { |
|
141 __exposedProps__: { |
|
142 alarmId: 'r', |
|
143 network: 'r', |
|
144 threshold: 'r', |
|
145 data: 'r', |
|
146 }, |
|
147 |
|
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}), |
|
154 |
|
155 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsAlarm]) |
|
156 }; |
|
157 |
|
158 // NetworkStatsManager |
|
159 |
|
160 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1"; |
|
161 const NETWORKSTATSMANAGER_CID = Components.ID("{8a66f4c1-0c25-4a66-9fc5-0106947b91f9}"); |
|
162 const nsIDOMMozNetworkStatsManager = Ci.nsIDOMMozNetworkStatsManager; |
|
163 |
|
164 function NetworkStatsManager() { |
|
165 if (DEBUG) { |
|
166 debug("Constructor"); |
|
167 } |
|
168 } |
|
169 |
|
170 NetworkStatsManager.prototype = { |
|
171 __proto__: DOMRequestIpcHelper.prototype, |
|
172 |
|
173 checkPrivileges: function checkPrivileges() { |
|
174 if (!this.hasPrivileges) { |
|
175 throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE); |
|
176 } |
|
177 }, |
|
178 |
|
179 getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) { |
|
180 this.checkPrivileges(); |
|
181 |
|
182 if (aStart.constructor.name !== "Date" || |
|
183 aEnd.constructor.name !== "Date" || |
|
184 aStart > aEnd) { |
|
185 throw Components.results.NS_ERROR_INVALID_ARG; |
|
186 } |
|
187 |
|
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 } |
|
197 |
|
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(); |
|
202 |
|
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 }, |
|
213 |
|
214 clearStats: function clearStats(aNetwork) { |
|
215 this.checkPrivileges(); |
|
216 |
|
217 let request = this.createRequest(); |
|
218 cpmm.sendAsyncMessage("NetworkStats:Clear", |
|
219 { network: aNetwork, |
|
220 id: this.getRequestId(request) }); |
|
221 return request; |
|
222 }, |
|
223 |
|
224 clearAllStats: function clearAllStats() { |
|
225 this.checkPrivileges(); |
|
226 |
|
227 let request = this.createRequest(); |
|
228 cpmm.sendAsyncMessage("NetworkStats:ClearAll", |
|
229 {id: this.getRequestId(request)}); |
|
230 return request; |
|
231 }, |
|
232 |
|
233 addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) { |
|
234 this.checkPrivileges(); |
|
235 |
|
236 if (!aOptions) { |
|
237 aOptions = Object.create(null); |
|
238 } |
|
239 |
|
240 if (aOptions.startTime && aOptions.startTime.constructor.name !== "Date") { |
|
241 throw Components.results.NS_ERROR_INVALID_ARG; |
|
242 } |
|
243 |
|
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 }, |
|
255 |
|
256 getAllAlarms: function getAllAlarms(aNetwork) { |
|
257 this.checkPrivileges(); |
|
258 |
|
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 }, |
|
266 |
|
267 removeAlarms: function removeAlarms(aAlarmId) { |
|
268 this.checkPrivileges(); |
|
269 |
|
270 if (aAlarmId == 0) { |
|
271 aAlarmId = -1; |
|
272 } |
|
273 |
|
274 let request = this.createRequest(); |
|
275 cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms", |
|
276 {id: this.getRequestId(request), |
|
277 data: {alarmId: aAlarmId, |
|
278 manifestURL: this.manifestURL}}); |
|
279 |
|
280 return request; |
|
281 }, |
|
282 |
|
283 getAvailableNetworks: function getAvailableNetworks() { |
|
284 this.checkPrivileges(); |
|
285 |
|
286 let request = this.createRequest(); |
|
287 cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks", |
|
288 { id: this.getRequestId(request) }); |
|
289 return request; |
|
290 }, |
|
291 |
|
292 getAvailableServiceTypes: function getAvailableServiceTypes() { |
|
293 this.checkPrivileges(); |
|
294 |
|
295 let request = this.createRequest(); |
|
296 cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes", |
|
297 { id: this.getRequestId(request) }); |
|
298 return request; |
|
299 }, |
|
300 |
|
301 get sampleRate() { |
|
302 this.checkPrivileges(); |
|
303 return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0]; |
|
304 }, |
|
305 |
|
306 get maxStorageAge() { |
|
307 this.checkPrivileges(); |
|
308 return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0]; |
|
309 }, |
|
310 |
|
311 receiveMessage: function(aMessage) { |
|
312 if (DEBUG) { |
|
313 debug("NetworkStatsmanager::receiveMessage: " + aMessage.name); |
|
314 } |
|
315 |
|
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 } |
|
324 |
|
325 switch (aMessage.name) { |
|
326 case "NetworkStats:Get:Return": |
|
327 if (msg.error) { |
|
328 Services.DOMRequest.fireError(req, msg.error); |
|
329 return; |
|
330 } |
|
331 |
|
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; |
|
338 |
|
339 case "NetworkStats:GetAvailableNetworks:Return": |
|
340 if (msg.error) { |
|
341 Services.DOMRequest.fireError(req, msg.error); |
|
342 return; |
|
343 } |
|
344 |
|
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 } |
|
349 |
|
350 Services.DOMRequest.fireSuccess(req, networks); |
|
351 break; |
|
352 |
|
353 case "NetworkStats:GetAvailableServiceTypes:Return": |
|
354 if (msg.error) { |
|
355 Services.DOMRequest.fireError(req, msg.error); |
|
356 return; |
|
357 } |
|
358 |
|
359 let serviceTypes = new this._window.Array(); |
|
360 for (let i = 0; i < msg.result.length; i++) { |
|
361 serviceTypes.push(msg.result[i]); |
|
362 } |
|
363 |
|
364 Services.DOMRequest.fireSuccess(req, serviceTypes); |
|
365 break; |
|
366 |
|
367 case "NetworkStats:Clear:Return": |
|
368 case "NetworkStats:ClearAll:Return": |
|
369 if (msg.error) { |
|
370 Services.DOMRequest.fireError(req, msg.error); |
|
371 return; |
|
372 } |
|
373 |
|
374 Services.DOMRequest.fireSuccess(req, true); |
|
375 break; |
|
376 |
|
377 case "NetworkStats:SetAlarm:Return": |
|
378 case "NetworkStats:RemoveAlarms:Return": |
|
379 if (msg.error) { |
|
380 Services.DOMRequest.fireError(req, msg.error); |
|
381 return; |
|
382 } |
|
383 |
|
384 Services.DOMRequest.fireSuccess(req, msg.result); |
|
385 break; |
|
386 |
|
387 case "NetworkStats:GetAlarms:Return": |
|
388 if (msg.error) { |
|
389 Services.DOMRequest.fireError(req, msg.error); |
|
390 return; |
|
391 } |
|
392 |
|
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 } |
|
397 |
|
398 Services.DOMRequest.fireSuccess(req, alarms); |
|
399 break; |
|
400 |
|
401 default: |
|
402 if (DEBUG) { |
|
403 debug("Wrong message: " + aMessage.name); |
|
404 } |
|
405 } |
|
406 }, |
|
407 |
|
408 init: function(aWindow) { |
|
409 // Set navigator.mozNetworkStats to null. |
|
410 if (!Services.prefs.getBoolPref("dom.mozNetworkStats.enabled")) { |
|
411 return null; |
|
412 } |
|
413 |
|
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"); |
|
420 |
|
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 } |
|
426 |
|
427 if (!this.hasPrivileges) { |
|
428 return null; |
|
429 } |
|
430 |
|
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"]); |
|
439 |
|
440 // Init app properties. |
|
441 let appsService = Cc["@mozilla.org/AppsService;1"] |
|
442 .getService(Ci.nsIAppsService); |
|
443 |
|
444 this.manifestURL = appsService.getManifestURLByLocalId(principal.appId); |
|
445 |
|
446 let isApp = !!this.manifestURL.length; |
|
447 if (isApp) { |
|
448 this.pageURL = principal.URI.spec; |
|
449 } |
|
450 }, |
|
451 |
|
452 // Called from DOMRequestIpcHelper |
|
453 uninit: function uninit() { |
|
454 if (DEBUG) { |
|
455 debug("uninit call"); |
|
456 } |
|
457 }, |
|
458 |
|
459 classID : NETWORKSTATSMANAGER_CID, |
|
460 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager, |
|
461 Ci.nsIDOMGlobalPropertyInitializer, |
|
462 Ci.nsISupportsWeakReference, |
|
463 Ci.nsIObserver]), |
|
464 |
|
465 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID, |
|
466 contractID: NETWORKSTATSMANAGER_CONTRACTID, |
|
467 classDescription: "NetworkStatsManager", |
|
468 interfaces: [nsIDOMMozNetworkStatsManager], |
|
469 flags: nsIClassInfo.DOM_OBJECT}) |
|
470 } |
|
471 |
|
472 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm, |
|
473 NetworkStatsData, |
|
474 NetworkStatsInterface, |
|
475 NetworkStats, |
|
476 NetworkStatsManager]); |