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/. */
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");
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() {
51 debug("NetworkStatsInterface Constructor");
55 NetworkStatsInterface.prototype = {
56 __init: function(aNetwork) {
57 this.type = aNetwork.type;
58 this.id = aNetwork.id;
61 classID : NETWORKSTATSINTERFACE_CID,
63 contractID: NETWORKSTATSINTERFACE_CONTRACTID,
64 QueryInterface : XPCOMUtils.generateQI([])
68 const NETWORKSTATS_CID = Components.ID("{28904f59-8497-4ac0-904f-2af14b7fd3de}");
70 function NetworkStats(aWindow, aStats) {
72 debug("NetworkStats Constructor");
74 this.appManifestURL = aStats.appManifestURL || null;
75 this.browsingTrafficOnly = aStats.browsingTrafficOnly || false;
76 this.serviceType = aStats.serviceType || null;
77 this.network = new aWindow.MozNetworkStatsInterface(aStats.network);
78 this.start = aStats.start ? new aWindow.Date(aStats.start.getTime()) : null;
79 this.end = aStats.end ? new aWindow.Date(aStats.end.getTime()) : null;
81 let samples = this.data = new aWindow.Array();
82 for (let i = 0; i < aStats.data.length; i++) {
83 samples.push(aWindow.MozNetworkStatsData._create(
84 aWindow, new NetworkStatsData(aWindow, aStats.data[i])));
88 NetworkStats.prototype = {
89 classID : NETWORKSTATS_CID,
91 QueryInterface : XPCOMUtils.generateQI()
95 const NETWORKSTATSALARM_CID = Components.ID("{a93ea13e-409c-4189-9b1e-95fff220be55}");
97 function NetworkStatsAlarm(aWindow, aAlarm) {
98 this.alarmId = aAlarm.id;
99 this.network = new aWindow.MozNetworkStatsInterface(aAlarm.network);
100 this.threshold = aAlarm.threshold;
101 this.data = aAlarm.data;
104 NetworkStatsAlarm.prototype = {
105 classID : NETWORKSTATSALARM_CID,
107 QueryInterface : XPCOMUtils.generateQI([])
110 // NetworkStatsManager
112 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
113 const NETWORKSTATSMANAGER_CID = Components.ID("{ceb874cd-cc1a-4e65-b404-cc2d3e42425f}");
114 const nsIDOMMozNetworkStatsManager = Ci.nsIDOMMozNetworkStatsManager;
116 function NetworkStatsManager() {
118 debug("Constructor");
122 NetworkStatsManager.prototype = {
123 __proto__: DOMRequestIpcHelper.prototype,
125 checkPrivileges: function checkPrivileges() {
126 if (!this.hasPrivileges) {
127 throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE);
131 getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) {
132 this.checkPrivileges();
134 if (aStart.constructor.name !== "Date" ||
135 aEnd.constructor.name !== "Date" ||
136 !(aNetwork instanceof this.window.MozNetworkStatsInterface) ||
138 throw Components.results.NS_ERROR_INVALID_ARG;
141 let appManifestURL = null;
142 let browsingTrafficOnly = false;
143 let serviceType = null;
145 // appManifestURL is used to query network statistics by app;
146 // serviceType is used to query network statistics by system service.
147 // It is illegal to specify both of them at the same time.
148 if (aOptions.appManifestURL && aOptions.serviceType) {
149 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
151 // browsingTrafficOnly is meaningful only when querying by app.
152 if (!aOptions.appManifestURL && aOptions.browsingTrafficOnly) {
153 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
155 appManifestURL = aOptions.appManifestURL;
156 serviceType = aOptions.serviceType;
157 browsingTrafficOnly = aOptions.browsingTrafficOnly || false;
160 // TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC
161 // This is just a work-around by passing timestamp numbers.
162 aStart = aStart.getTime();
163 aEnd = aEnd.getTime();
165 let request = this.createRequest();
166 cpmm.sendAsyncMessage("NetworkStats:Get",
167 { network: aNetwork.toJSON(),
170 appManifestURL: appManifestURL,
171 browsingTrafficOnly: browsingTrafficOnly,
172 serviceType: serviceType,
173 id: this.getRequestId(request) });
177 clearStats: function clearStats(aNetwork) {
178 this.checkPrivileges();
180 if (!aNetwork instanceof this.window.MozNetworkStatsInterface) {
181 throw Components.results.NS_ERROR_INVALID_ARG;
184 let request = this.createRequest();
185 cpmm.sendAsyncMessage("NetworkStats:Clear",
186 { network: aNetwork.toJSON(),
187 id: this.getRequestId(request) });
191 clearAllStats: function clearAllStats() {
192 this.checkPrivileges();
194 let request = this.createRequest();
195 cpmm.sendAsyncMessage("NetworkStats:ClearAll",
196 {id: this.getRequestId(request)});
200 addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
201 this.checkPrivileges();
204 aOptions = Object.create(null);
207 if (aOptions.startTime && aOptions.startTime.constructor.name !== "Date" ||
208 !(aNetwork instanceof this.window.MozNetworkStatsInterface)) {
209 throw Components.results.NS_ERROR_INVALID_ARG;
212 let request = this.createRequest();
213 cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
214 {id: this.getRequestId(request),
215 data: {network: aNetwork.toJSON(),
216 threshold: aThreshold,
217 startTime: aOptions.startTime,
219 manifestURL: this.manifestURL,
220 pageURL: this.pageURL}});
224 getAllAlarms: function getAllAlarms(aNetwork) {
225 this.checkPrivileges();
229 if (!aNetwork instanceof this.window.MozNetworkStatsInterface) {
230 throw Components.results.NS_ERROR_INVALID_ARG;
232 network = aNetwork.toJSON();
235 let request = this.createRequest();
236 cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
237 {id: this.getRequestId(request),
238 data: {network: network,
239 manifestURL: this.manifestURL}});
243 removeAlarms: function removeAlarms(aAlarmId) {
244 this.checkPrivileges();
250 let request = this.createRequest();
251 cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
252 {id: this.getRequestId(request),
253 data: {alarmId: aAlarmId,
254 manifestURL: this.manifestURL}});
259 getAvailableNetworks: function getAvailableNetworks() {
260 this.checkPrivileges();
262 let request = this.createRequest();
263 cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks",
264 { id: this.getRequestId(request) });
268 getAvailableServiceTypes: function getAvailableServiceTypes() {
269 this.checkPrivileges();
271 let request = this.createRequest();
272 cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes",
273 { id: this.getRequestId(request) });
278 this.checkPrivileges();
279 return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
282 get maxStorageAge() {
283 this.checkPrivileges();
284 return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
287 receiveMessage: function(aMessage) {
289 debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
292 let msg = aMessage.json;
293 let req = this.takeRequest(msg.id);
296 debug("No request stored with id " + msg.id);
301 switch (aMessage.name) {
302 case "NetworkStats:Get:Return":
304 Services.DOMRequest.fireError(req, msg.error);
308 let result = this._window.MozNetworkStats._create(
309 this._window, new NetworkStats(this._window, msg.result));
311 debug("result: " + JSON.stringify(result));
313 Services.DOMRequest.fireSuccess(req, result);
316 case "NetworkStats:GetAvailableNetworks:Return":
318 Services.DOMRequest.fireError(req, msg.error);
322 let networks = new this._window.Array();
323 for (let i = 0; i < msg.result.length; i++) {
324 let network = new this._window.MozNetworkStatsInterface(msg.result[i]);
325 networks.push(network);
328 Services.DOMRequest.fireSuccess(req, networks);
331 case "NetworkStats:GetAvailableServiceTypes:Return":
333 Services.DOMRequest.fireError(req, msg.error);
337 let serviceTypes = new this._window.Array();
338 for (let i = 0; i < msg.result.length; i++) {
339 serviceTypes.push(msg.result[i]);
342 Services.DOMRequest.fireSuccess(req, serviceTypes);
345 case "NetworkStats:Clear:Return":
346 case "NetworkStats:ClearAll:Return":
348 Services.DOMRequest.fireError(req, msg.error);
352 Services.DOMRequest.fireSuccess(req, true);
355 case "NetworkStats:SetAlarm:Return":
356 case "NetworkStats:RemoveAlarms:Return":
358 Services.DOMRequest.fireError(req, msg.error);
362 Services.DOMRequest.fireSuccess(req, msg.result);
365 case "NetworkStats:GetAlarms:Return":
367 Services.DOMRequest.fireError(req, msg.error);
371 let alarms = new this._window.Array();
372 for (let i = 0; i < msg.result.length; i++) {
373 // The WebIDL type of data is any, so we should manually clone it
374 // into the content window.
375 if ("data" in msg.result[i]) {
376 msg.result[i].data = Cu.cloneInto(msg.result[i].data, this._window);
378 let alarm = new NetworkStatsAlarm(this._window, msg.result[i]);
379 alarms.push(this._window.MozNetworkStatsAlarm._create(this._window, alarm));
382 Services.DOMRequest.fireSuccess(req, alarms);
387 debug("Wrong message: " + aMessage.name);
392 init: function(aWindow) {
393 // Set navigator.mozNetworkStats to null.
394 if (!Services.prefs.getBoolPref("dom.mozNetworkStats.enabled")) {
398 let principal = aWindow.document.nodePrincipal;
399 let secMan = Services.scriptSecurityManager;
400 let perm = principal == secMan.getSystemPrincipal() ?
401 Ci.nsIPermissionManager.ALLOW_ACTION :
402 Services.perms.testExactPermissionFromPrincipal(principal,
403 "networkstats-manage");
405 // Only pages with perm set can use the netstats.
406 this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
408 debug("has privileges: " + this.hasPrivileges);
411 if (!this.hasPrivileges) {
415 this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
416 "NetworkStats:GetAvailableNetworks:Return",
417 "NetworkStats:GetAvailableServiceTypes:Return",
418 "NetworkStats:Clear:Return",
419 "NetworkStats:ClearAll:Return",
420 "NetworkStats:SetAlarm:Return",
421 "NetworkStats:GetAlarms:Return",
422 "NetworkStats:RemoveAlarms:Return"]);
424 // Init app properties.
425 let appsService = Cc["@mozilla.org/AppsService;1"]
426 .getService(Ci.nsIAppsService);
428 this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
430 let isApp = !!this.manifestURL.length;
432 this.pageURL = principal.URI.spec;
435 this.window = aWindow;
438 // Called from DOMRequestIpcHelper
439 uninit: function uninit() {
441 debug("uninit call");
445 classID : NETWORKSTATSMANAGER_CID,
446 QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager,
447 Ci.nsIDOMGlobalPropertyInitializer,
448 Ci.nsISupportsWeakReference,
451 classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
452 contractID: NETWORKSTATSMANAGER_CONTRACTID,
453 classDescription: "NetworkStatsManager",
454 interfaces: [nsIDOMMozNetworkStatsManager],
455 flags: nsIClassInfo.DOM_OBJECT})
458 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
460 NetworkStatsInterface,
462 NetworkStatsManager]);