michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const DEBUG = false; michael@0: function debug(s) { dump("-*- NetworkStatsManager: " + s + "\n"); } michael@0: michael@0: const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); michael@0: michael@0: // Ensure NetworkStatsService and NetworkStatsDB are loaded in the parent process michael@0: // to receive messages from the child processes. michael@0: let appInfo = Cc["@mozilla.org/xre/app-info;1"]; michael@0: let isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime) michael@0: .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; michael@0: if (isParentProcess) { michael@0: Cu.import("resource://gre/modules/NetworkStatsService.jsm"); michael@0: } michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "cpmm", michael@0: "@mozilla.org/childprocessmessagemanager;1", michael@0: "nsISyncMessageSender"); michael@0: michael@0: // NetworkStatsData michael@0: const nsIClassInfo = Ci.nsIClassInfo; michael@0: const NETWORKSTATSDATA_CID = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}"); michael@0: const nsIDOMMozNetworkStatsData = Ci.nsIDOMMozNetworkStatsData; michael@0: michael@0: function NetworkStatsData(aWindow, aData) { michael@0: this.rxBytes = aData.rxBytes; michael@0: this.txBytes = aData.txBytes; michael@0: this.date = new aWindow.Date(aData.date.getTime()); michael@0: } michael@0: michael@0: NetworkStatsData.prototype = { michael@0: __exposedProps__: { michael@0: rxBytes: 'r', michael@0: txBytes: 'r', michael@0: date: 'r', michael@0: }, michael@0: michael@0: classID : NETWORKSTATSDATA_CID, michael@0: classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSDATA_CID, michael@0: contractID:"@mozilla.org/networkstatsdata;1", michael@0: classDescription: "NetworkStatsData", michael@0: interfaces: [nsIDOMMozNetworkStatsData], michael@0: flags: nsIClassInfo.DOM_OBJECT}), michael@0: michael@0: QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData]) michael@0: }; michael@0: michael@0: // NetworkStatsInterface michael@0: const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1"; michael@0: const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}"); michael@0: const nsIDOMMozNetworkStatsInterface = Ci.nsIDOMMozNetworkStatsInterface; michael@0: michael@0: function NetworkStatsInterface(aNetwork) { michael@0: if (DEBUG) { michael@0: debug("NetworkStatsInterface Constructor"); michael@0: } michael@0: this.type = aNetwork.type; michael@0: this.id = aNetwork.id; michael@0: } michael@0: michael@0: NetworkStatsInterface.prototype = { michael@0: __exposedProps__: { michael@0: id: 'r', michael@0: type: 'r', michael@0: }, michael@0: michael@0: classID : NETWORKSTATSINTERFACE_CID, michael@0: classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID, michael@0: contractID: NETWORKSTATSINTERFACE_CONTRACTID, michael@0: classDescription: "NetworkStatsInterface", michael@0: interfaces: [nsIDOMMozNetworkStatsInterface], michael@0: flags: nsIClassInfo.DOM_OBJECT}), michael@0: michael@0: QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface]) michael@0: } michael@0: michael@0: // NetworkStats michael@0: const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1"; michael@0: const NETWORKSTATS_CID = Components.ID("{f1996e44-1057-4d4b-8ff8-919e76c4cfa9}"); michael@0: const nsIDOMMozNetworkStats = Ci.nsIDOMMozNetworkStats; michael@0: michael@0: function NetworkStats(aWindow, aStats) { michael@0: if (DEBUG) { michael@0: debug("NetworkStats Constructor"); michael@0: } michael@0: this.appManifestURL = aStats.appManifestURL || null; michael@0: this.serviceType = aStats.serviceType || null; michael@0: this.network = new NetworkStatsInterface(aStats.network); michael@0: this.start = aStats.start ? new aWindow.Date(aStats.start.getTime()) : null; michael@0: this.end = aStats.end ? new aWindow.Date(aStats.end.getTime()) : null; michael@0: michael@0: let samples = this.data = new aWindow.Array(); michael@0: for (let i = 0; i < aStats.data.length; i++) { michael@0: samples.push(new NetworkStatsData(aWindow, aStats.data[i])); michael@0: } michael@0: } michael@0: michael@0: NetworkStats.prototype = { michael@0: __exposedProps__: { michael@0: appManifestURL: 'r', michael@0: serviceType: 'r', michael@0: network: 'r', michael@0: start: 'r', michael@0: end: 'r', michael@0: data: 'r', michael@0: }, michael@0: michael@0: classID : NETWORKSTATS_CID, michael@0: classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID, michael@0: contractID: NETWORKSTATS_CONTRACTID, michael@0: classDescription: "NetworkStats", michael@0: interfaces: [nsIDOMMozNetworkStats], michael@0: flags: nsIClassInfo.DOM_OBJECT}), michael@0: michael@0: QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats, michael@0: nsIDOMMozNetworkStatsData, michael@0: nsIDOMMozNetworkStatsInterface]) michael@0: } michael@0: michael@0: // NetworkStatsAlarm michael@0: const NETWORKSTATSALARM_CID = Components.ID("{063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}"); michael@0: const nsIDOMMozNetworkStatsAlarm = Ci.nsIDOMMozNetworkStatsAlarm; michael@0: michael@0: function NetworkStatsAlarm(aAlarm) { michael@0: this.alarmId = aAlarm.id; michael@0: this.network = new NetworkStatsInterface(aAlarm.network); michael@0: this.threshold = aAlarm.threshold; michael@0: this.data = aAlarm.data; michael@0: } michael@0: michael@0: NetworkStatsAlarm.prototype = { michael@0: __exposedProps__: { michael@0: alarmId: 'r', michael@0: network: 'r', michael@0: threshold: 'r', michael@0: data: 'r', michael@0: }, michael@0: michael@0: classID : NETWORKSTATSALARM_CID, michael@0: classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSALARM_CID, michael@0: contractID:"@mozilla.org/networkstatsalarm;1", michael@0: classDescription: "NetworkStatsAlarm", michael@0: interfaces: [nsIDOMMozNetworkStatsAlarm], michael@0: flags: nsIClassInfo.DOM_OBJECT}), michael@0: michael@0: QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsAlarm]) michael@0: }; michael@0: michael@0: // NetworkStatsManager michael@0: michael@0: const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1"; michael@0: const NETWORKSTATSMANAGER_CID = Components.ID("{8a66f4c1-0c25-4a66-9fc5-0106947b91f9}"); michael@0: const nsIDOMMozNetworkStatsManager = Ci.nsIDOMMozNetworkStatsManager; michael@0: michael@0: function NetworkStatsManager() { michael@0: if (DEBUG) { michael@0: debug("Constructor"); michael@0: } michael@0: } michael@0: michael@0: NetworkStatsManager.prototype = { michael@0: __proto__: DOMRequestIpcHelper.prototype, michael@0: michael@0: checkPrivileges: function checkPrivileges() { michael@0: if (!this.hasPrivileges) { michael@0: throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE); michael@0: } michael@0: }, michael@0: michael@0: getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) { michael@0: this.checkPrivileges(); michael@0: michael@0: if (aStart.constructor.name !== "Date" || michael@0: aEnd.constructor.name !== "Date" || michael@0: aStart > aEnd) { michael@0: throw Components.results.NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: let appManifestURL = null; michael@0: let serviceType = null; michael@0: if (aOptions) { michael@0: if (aOptions.appManifestURL && aOptions.serviceType) { michael@0: throw Components.results.NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: appManifestURL = aOptions.appManifestURL; michael@0: serviceType = aOptions.serviceType; michael@0: } michael@0: michael@0: // TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC michael@0: // This is just a work-around by passing timestamp numbers. michael@0: aStart = aStart.getTime(); michael@0: aEnd = aEnd.getTime(); michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:Get", michael@0: { network: aNetwork, michael@0: start: aStart, michael@0: end: aEnd, michael@0: appManifestURL: appManifestURL, michael@0: serviceType: serviceType, michael@0: id: this.getRequestId(request) }); michael@0: return request; michael@0: }, michael@0: michael@0: clearStats: function clearStats(aNetwork) { michael@0: this.checkPrivileges(); michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:Clear", michael@0: { network: aNetwork, michael@0: id: this.getRequestId(request) }); michael@0: return request; michael@0: }, michael@0: michael@0: clearAllStats: function clearAllStats() { michael@0: this.checkPrivileges(); michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:ClearAll", michael@0: {id: this.getRequestId(request)}); michael@0: return request; michael@0: }, michael@0: michael@0: addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) { michael@0: this.checkPrivileges(); michael@0: michael@0: if (!aOptions) { michael@0: aOptions = Object.create(null); michael@0: } michael@0: michael@0: if (aOptions.startTime && aOptions.startTime.constructor.name !== "Date") { michael@0: throw Components.results.NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:SetAlarm", michael@0: {id: this.getRequestId(request), michael@0: data: {network: aNetwork, michael@0: threshold: aThreshold, michael@0: startTime: aOptions.startTime, michael@0: data: aOptions.data, michael@0: manifestURL: this.manifestURL, michael@0: pageURL: this.pageURL}}); michael@0: return request; michael@0: }, michael@0: michael@0: getAllAlarms: function getAllAlarms(aNetwork) { michael@0: this.checkPrivileges(); michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:GetAlarms", michael@0: {id: this.getRequestId(request), michael@0: data: {network: aNetwork, michael@0: manifestURL: this.manifestURL}}); michael@0: return request; michael@0: }, michael@0: michael@0: removeAlarms: function removeAlarms(aAlarmId) { michael@0: this.checkPrivileges(); michael@0: michael@0: if (aAlarmId == 0) { michael@0: aAlarmId = -1; michael@0: } michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms", michael@0: {id: this.getRequestId(request), michael@0: data: {alarmId: aAlarmId, michael@0: manifestURL: this.manifestURL}}); michael@0: michael@0: return request; michael@0: }, michael@0: michael@0: getAvailableNetworks: function getAvailableNetworks() { michael@0: this.checkPrivileges(); michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks", michael@0: { id: this.getRequestId(request) }); michael@0: return request; michael@0: }, michael@0: michael@0: getAvailableServiceTypes: function getAvailableServiceTypes() { michael@0: this.checkPrivileges(); michael@0: michael@0: let request = this.createRequest(); michael@0: cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes", michael@0: { id: this.getRequestId(request) }); michael@0: return request; michael@0: }, michael@0: michael@0: get sampleRate() { michael@0: this.checkPrivileges(); michael@0: return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0]; michael@0: }, michael@0: michael@0: get maxStorageAge() { michael@0: this.checkPrivileges(); michael@0: return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0]; michael@0: }, michael@0: michael@0: receiveMessage: function(aMessage) { michael@0: if (DEBUG) { michael@0: debug("NetworkStatsmanager::receiveMessage: " + aMessage.name); michael@0: } michael@0: michael@0: let msg = aMessage.json; michael@0: let req = this.takeRequest(msg.id); michael@0: if (!req) { michael@0: if (DEBUG) { michael@0: debug("No request stored with id " + msg.id); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: switch (aMessage.name) { michael@0: case "NetworkStats:Get:Return": michael@0: if (msg.error) { michael@0: Services.DOMRequest.fireError(req, msg.error); michael@0: return; michael@0: } michael@0: michael@0: let result = new NetworkStats(this._window, msg.result); michael@0: if (DEBUG) { michael@0: debug("result: " + JSON.stringify(result)); michael@0: } michael@0: Services.DOMRequest.fireSuccess(req, result); michael@0: break; michael@0: michael@0: case "NetworkStats:GetAvailableNetworks:Return": michael@0: if (msg.error) { michael@0: Services.DOMRequest.fireError(req, msg.error); michael@0: return; michael@0: } michael@0: michael@0: let networks = new this._window.Array(); michael@0: for (let i = 0; i < msg.result.length; i++) { michael@0: networks.push(new NetworkStatsInterface(msg.result[i])); michael@0: } michael@0: michael@0: Services.DOMRequest.fireSuccess(req, networks); michael@0: break; michael@0: michael@0: case "NetworkStats:GetAvailableServiceTypes:Return": michael@0: if (msg.error) { michael@0: Services.DOMRequest.fireError(req, msg.error); michael@0: return; michael@0: } michael@0: michael@0: let serviceTypes = new this._window.Array(); michael@0: for (let i = 0; i < msg.result.length; i++) { michael@0: serviceTypes.push(msg.result[i]); michael@0: } michael@0: michael@0: Services.DOMRequest.fireSuccess(req, serviceTypes); michael@0: break; michael@0: michael@0: case "NetworkStats:Clear:Return": michael@0: case "NetworkStats:ClearAll:Return": michael@0: if (msg.error) { michael@0: Services.DOMRequest.fireError(req, msg.error); michael@0: return; michael@0: } michael@0: michael@0: Services.DOMRequest.fireSuccess(req, true); michael@0: break; michael@0: michael@0: case "NetworkStats:SetAlarm:Return": michael@0: case "NetworkStats:RemoveAlarms:Return": michael@0: if (msg.error) { michael@0: Services.DOMRequest.fireError(req, msg.error); michael@0: return; michael@0: } michael@0: michael@0: Services.DOMRequest.fireSuccess(req, msg.result); michael@0: break; michael@0: michael@0: case "NetworkStats:GetAlarms:Return": michael@0: if (msg.error) { michael@0: Services.DOMRequest.fireError(req, msg.error); michael@0: return; michael@0: } michael@0: michael@0: let alarms = new this._window.Array(); michael@0: for (let i = 0; i < msg.result.length; i++) { michael@0: alarms.push(new NetworkStatsAlarm(msg.result[i])); michael@0: } michael@0: michael@0: Services.DOMRequest.fireSuccess(req, alarms); michael@0: break; michael@0: michael@0: default: michael@0: if (DEBUG) { michael@0: debug("Wrong message: " + aMessage.name); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: init: function(aWindow) { michael@0: // Set navigator.mozNetworkStats to null. michael@0: if (!Services.prefs.getBoolPref("dom.mozNetworkStats.enabled")) { michael@0: return null; michael@0: } michael@0: michael@0: let principal = aWindow.document.nodePrincipal; michael@0: let secMan = Services.scriptSecurityManager; michael@0: let perm = principal == secMan.getSystemPrincipal() ? michael@0: Ci.nsIPermissionManager.ALLOW_ACTION : michael@0: Services.perms.testExactPermissionFromPrincipal(principal, michael@0: "networkstats-manage"); michael@0: michael@0: // Only pages with perm set can use the netstats. michael@0: this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION; michael@0: if (DEBUG) { michael@0: debug("has privileges: " + this.hasPrivileges); michael@0: } michael@0: michael@0: if (!this.hasPrivileges) { michael@0: return null; michael@0: } michael@0: michael@0: this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return", michael@0: "NetworkStats:GetAvailableNetworks:Return", michael@0: "NetworkStats:GetAvailableServiceTypes:Return", michael@0: "NetworkStats:Clear:Return", michael@0: "NetworkStats:ClearAll:Return", michael@0: "NetworkStats:SetAlarm:Return", michael@0: "NetworkStats:GetAlarms:Return", michael@0: "NetworkStats:RemoveAlarms:Return"]); michael@0: michael@0: // Init app properties. michael@0: let appsService = Cc["@mozilla.org/AppsService;1"] michael@0: .getService(Ci.nsIAppsService); michael@0: michael@0: this.manifestURL = appsService.getManifestURLByLocalId(principal.appId); michael@0: michael@0: let isApp = !!this.manifestURL.length; michael@0: if (isApp) { michael@0: this.pageURL = principal.URI.spec; michael@0: } michael@0: }, michael@0: michael@0: // Called from DOMRequestIpcHelper michael@0: uninit: function uninit() { michael@0: if (DEBUG) { michael@0: debug("uninit call"); michael@0: } michael@0: }, michael@0: michael@0: classID : NETWORKSTATSMANAGER_CID, michael@0: QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager, michael@0: Ci.nsIDOMGlobalPropertyInitializer, michael@0: Ci.nsISupportsWeakReference, michael@0: Ci.nsIObserver]), michael@0: michael@0: classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID, michael@0: contractID: NETWORKSTATSMANAGER_CONTRACTID, michael@0: classDescription: "NetworkStatsManager", michael@0: interfaces: [nsIDOMMozNetworkStatsManager], michael@0: flags: nsIClassInfo.DOM_OBJECT}) michael@0: } michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm, michael@0: NetworkStatsData, michael@0: NetworkStatsInterface, michael@0: NetworkStats, michael@0: NetworkStatsManager]);