mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 951976 - Part 1: WebIDL implementation. r=gene
This commit is contained in:
parent
1998d0ab74
commit
c456bdd682
@ -481,6 +481,11 @@ pref("dom.mozNetworkStats.enabled", true);
|
|||||||
pref("dom.webapps.firstRunWithSIM", true);
|
pref("dom.webapps.firstRunWithSIM", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ResourceStats
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
pref("dom.resource_stats.enabled", true);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef MOZ_B2G_RIL
|
#ifdef MOZ_B2G_RIL
|
||||||
// SingleVariant
|
// SingleVariant
|
||||||
pref("dom.mozApps.single_variant_sourcedir", "/persist/svoperapps");
|
pref("dom.mozApps.single_variant_sourcedir", "/persist/svoperapps");
|
||||||
|
@ -19,6 +19,7 @@ Cu.import('resource://gre/modules/ErrorPage.jsm');
|
|||||||
Cu.import('resource://gre/modules/AlertsHelper.jsm');
|
Cu.import('resource://gre/modules/AlertsHelper.jsm');
|
||||||
#ifdef MOZ_WIDGET_GONK
|
#ifdef MOZ_WIDGET_GONK
|
||||||
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
|
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
|
||||||
|
Cu.import('resource://gre/modules/ResourceStatsService.jsm');
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Identity
|
// Identity
|
||||||
|
@ -421,6 +421,14 @@
|
|||||||
@BINPATH@/components/WifiWorker.manifest
|
@BINPATH@/components/WifiWorker.manifest
|
||||||
#endif // MOZ_WIDGET_GONK
|
#endif // MOZ_WIDGET_GONK
|
||||||
|
|
||||||
|
; ResourceStats
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
@BINPATH@/components/ResourceStats.js
|
||||||
|
@BINPATH@/components/ResourceStats.manifest
|
||||||
|
@BINPATH@/components/ResourceStatsManager.js
|
||||||
|
@BINPATH@/components/ResourceStatsManager.manifest
|
||||||
|
#endif // MOZ_WIDGET_GONK
|
||||||
|
|
||||||
; RIL
|
; RIL
|
||||||
#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
|
#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
|
||||||
@BINPATH@/components/MmsService.js
|
@BINPATH@/components/MmsService.js
|
||||||
|
@ -186,6 +186,11 @@ this.PermissionsTable = { geolocation: {
|
|||||||
privileged: DENY_ACTION,
|
privileged: DENY_ACTION,
|
||||||
certified: ALLOW_ACTION
|
certified: ALLOW_ACTION
|
||||||
},
|
},
|
||||||
|
"resourcestats-manage": {
|
||||||
|
app: DENY_ACTION,
|
||||||
|
privileged: DENY_ACTION,
|
||||||
|
certified: ALLOW_ACTION
|
||||||
|
},
|
||||||
"wifi-manage": {
|
"wifi-manage": {
|
||||||
app: DENY_ACTION,
|
app: DENY_ACTION,
|
||||||
privileged: DENY_ACTION,
|
privileged: DENY_ACTION,
|
||||||
|
@ -83,6 +83,7 @@ PARALLEL_DIRS += [
|
|||||||
'webidl',
|
'webidl',
|
||||||
'xbl',
|
'xbl',
|
||||||
'xslt',
|
'xslt',
|
||||||
|
'resourcestats',
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
|
39
dom/resourcestats/ResourceStatsControl.cpp
Normal file
39
dom/resourcestats/ResourceStatsControl.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "ResourceStatsControl.h"
|
||||||
|
#include "mozilla/Preferences.h"
|
||||||
|
#include "nsIPermissionManager.h"
|
||||||
|
#include "nsJSUtils.h"
|
||||||
|
#include "nsPIDOMWindow.h"
|
||||||
|
#include "nsServiceManagerUtils.h"
|
||||||
|
|
||||||
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
ResourceStatsControl::HasResourceStatsSupport(JSContext* /* unused */,
|
||||||
|
JSObject* aGlobal)
|
||||||
|
{
|
||||||
|
// Disable the constructors if they're disabled by the preference for sure.
|
||||||
|
if (!Preferences::GetBool("dom.resource_stats.enabled", false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get window.
|
||||||
|
nsCOMPtr<nsPIDOMWindow> win =
|
||||||
|
do_QueryInterface((nsISupports*)nsJSUtils::GetStaticScriptGlobal(aGlobal));
|
||||||
|
MOZ_ASSERT(!win || win->IsInnerWindow());
|
||||||
|
if (!win) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check permission.
|
||||||
|
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||||
|
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(permMgr, false);
|
||||||
|
|
||||||
|
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||||
|
permMgr->TestPermissionFromWindow(win, "resourcestats-manage", &permission);
|
||||||
|
return permission == nsIPermissionManager::ALLOW_ACTION;
|
||||||
|
}
|
25
dom/resourcestats/ResourceStatsControl.h
Normal file
25
dom/resourcestats/ResourceStatsControl.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_ResourceStatsConotrl_h
|
||||||
|
#define mozilla_dom_ResourceStatsConotrl_h
|
||||||
|
|
||||||
|
// Forward declarations.
|
||||||
|
struct JSContext;
|
||||||
|
class JSObject;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class ResourceStatsControl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool HasResourceStatsSupport(JSContext* /* unused */,
|
||||||
|
JSObject* aGlobal);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_ResourceStatsConotrl_h
|
537
dom/resourcestats/ResourceStatsDB.jsm
Normal file
537
dom/resourcestats/ResourceStatsDB.jsm
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ['ResourceStatsDB'];
|
||||||
|
|
||||||
|
const DEBUG = false;
|
||||||
|
function debug(s) { dump("-*- ResourceStatsDB: " + s + "\n"); }
|
||||||
|
|
||||||
|
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||||
|
"@mozilla.org/AppsService;1",
|
||||||
|
"nsIAppsService");
|
||||||
|
|
||||||
|
const DB_NAME = "resource_stats";
|
||||||
|
const DB_VERSION = 1;
|
||||||
|
const POWER_STATS_STORE = "power_stats_store";
|
||||||
|
const NETWORK_STATS_STORE = "network_stats_store";
|
||||||
|
const ALARM_STORE = "alarm_store";
|
||||||
|
|
||||||
|
const statsStoreNames = {
|
||||||
|
power: POWER_STATS_STORE,
|
||||||
|
network: NETWORK_STATS_STORE
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constant defining the sampling rate.
|
||||||
|
const SAMPLE_RATE = 24 * 60 * 60 * 1000; // 1 day.
|
||||||
|
|
||||||
|
// Constant defining the MAX age of stored stats.
|
||||||
|
const MAX_STORAGE_AGE = 180 * SAMPLE_RATE; // 180 days.
|
||||||
|
|
||||||
|
this.ResourceStatsDB = function ResourceStatsDB() {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Constructor()");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initDBHelper(DB_NAME, DB_VERSION,
|
||||||
|
[POWER_STATS_STORE, NETWORK_STATS_STORE, ALARM_STORE]);
|
||||||
|
};
|
||||||
|
|
||||||
|
ResourceStatsDB.prototype = {
|
||||||
|
__proto__: IndexedDBHelper.prototype,
|
||||||
|
|
||||||
|
_dbNewTxn: function(aStoreName, aTxnType, aCallback, aTxnCb) {
|
||||||
|
function successCb(aResult) {
|
||||||
|
aTxnCb(null, aResult);
|
||||||
|
}
|
||||||
|
function errorCb(aError) {
|
||||||
|
aTxnCb(aError, null);
|
||||||
|
}
|
||||||
|
return this.newTxn(aTxnType, aStoreName, aCallback, successCb, errorCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Upgrade DB from ver." + aOldVersion + " to ver." + aNewVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
let objectStore;
|
||||||
|
|
||||||
|
// Create PowerStatsStore.
|
||||||
|
objectStore = aDb.createObjectStore(POWER_STATS_STORE, {
|
||||||
|
keyPath: ["appId", "serviceType", "component", "timestamp"]
|
||||||
|
});
|
||||||
|
objectStore.createIndex("component", "component", { unique: false });
|
||||||
|
|
||||||
|
// Create NetworkStatsStore.
|
||||||
|
objectStore = aDb.createObjectStore(NETWORK_STATS_STORE, {
|
||||||
|
keyPath: ["appId", "serviceType", "component", "timestamp"]
|
||||||
|
});
|
||||||
|
objectStore.createIndex("component", "component", { unique: false });
|
||||||
|
|
||||||
|
// Create AlarmStore.
|
||||||
|
objectStore = aDb.createObjectStore(ALARM_STORE, {
|
||||||
|
keyPath: "alarmId",
|
||||||
|
autoIncrement: true
|
||||||
|
});
|
||||||
|
objectStore.createIndex("type", "type", { unique: false });
|
||||||
|
// Index for resource control target.
|
||||||
|
objectStore.createIndex("controlTarget",
|
||||||
|
["type", "manifestURL", "serviceType", "component"],
|
||||||
|
{ unique: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert to UTC according to the current timezone and the filter timestamp
|
||||||
|
// to get SAMPLE_RATE precission.
|
||||||
|
_normalizeTime: function(aTime, aOffset) {
|
||||||
|
let time = Math.floor((aTime - aOffset) / SAMPLE_RATE) * SAMPLE_RATE;
|
||||||
|
|
||||||
|
return time;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aRecordArray contains an array of json objects storing network stats.
|
||||||
|
* The structure of the json object =
|
||||||
|
* {
|
||||||
|
* appId: XX,
|
||||||
|
* serviceType: "XX",
|
||||||
|
* componentStats: {
|
||||||
|
* "component_1": { receivedBytes: XX, sentBytes: XX },
|
||||||
|
* "component_2": { receivedBytes: XX, sentBytes: XX },
|
||||||
|
* ...
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
saveNetworkStats: function(aRecordArray, aTimestamp, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("saveNetworkStats()");
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||||
|
let timestamp = this._normalizeTime(aTimestamp, offset);
|
||||||
|
|
||||||
|
this._dbNewTxn(NETWORK_STATS_STORE, "readwrite", function(aTxn, aStore) {
|
||||||
|
aRecordArray.forEach(function(aRecord) {
|
||||||
|
let stats = {
|
||||||
|
appId: aRecord.appId,
|
||||||
|
serviceType: aRecord.serviceType,
|
||||||
|
component: "",
|
||||||
|
timestamp: timestamp,
|
||||||
|
receivedBytes: 0,
|
||||||
|
sentBytes: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let totalReceivedBytes = 0;
|
||||||
|
let totalSentBytes = 0;
|
||||||
|
|
||||||
|
// Save stats of each component.
|
||||||
|
let data = aRecord.componentStats;
|
||||||
|
for (let component in data) {
|
||||||
|
// Save stats to database.
|
||||||
|
stats.component = component;
|
||||||
|
stats.receivedBytes = data[component].receivedBytes;
|
||||||
|
stats.sentBytes = data[component].sentBytes;
|
||||||
|
aStore.put(stats);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Save network stats: " + JSON.stringify(stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulated to tatal stats.
|
||||||
|
totalReceivedBytes += stats.receivedBytes;
|
||||||
|
totalSentBytes += stats.sentBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save total stats.
|
||||||
|
stats.component = "";
|
||||||
|
stats.receivedBytes = totalReceivedBytes;
|
||||||
|
stats.sentBytes = totalSentBytes;
|
||||||
|
aStore.put(stats);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Save network stats: " + JSON.stringify(stats));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aRecordArray contains an array of json objects storing power stats.
|
||||||
|
* The structure of the json object =
|
||||||
|
* {
|
||||||
|
* appId: XX,
|
||||||
|
* serviceType: "XX",
|
||||||
|
* componentStats: {
|
||||||
|
* "component_1": XX, // consumedPower
|
||||||
|
* "component_2": XX,
|
||||||
|
* ...
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
savePowerStats: function(aRecordArray, aTimestamp, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("savePowerStats()");
|
||||||
|
}
|
||||||
|
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||||
|
let timestamp = this._normalizeTime(aTimestamp, offset);
|
||||||
|
|
||||||
|
this._dbNewTxn(POWER_STATS_STORE, "readwrite", function(aTxn, aStore) {
|
||||||
|
aRecordArray.forEach(function(aRecord) {
|
||||||
|
let stats = {
|
||||||
|
appId: aRecord.appId,
|
||||||
|
serviceType: aRecord.serviceType,
|
||||||
|
component: "",
|
||||||
|
timestamp: timestamp,
|
||||||
|
consumedPower: aRecord.totalStats
|
||||||
|
};
|
||||||
|
|
||||||
|
let totalConsumedPower = 0;
|
||||||
|
|
||||||
|
// Save stats of each component to database.
|
||||||
|
let data = aRecord.componentStats;
|
||||||
|
for (let component in data) {
|
||||||
|
// Save stats to database.
|
||||||
|
stats.component = component;
|
||||||
|
stats.consumedPower = data[component];
|
||||||
|
aStore.put(stats);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Save power stats: " + JSON.stringify(stats));
|
||||||
|
}
|
||||||
|
// Accumulated to total stats.
|
||||||
|
totalConsumedPower += stats.consumedPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save total stats.
|
||||||
|
stats.component = "";
|
||||||
|
stats.consumedPower = totalConsumedPower;
|
||||||
|
aStore.put(stats);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Save power stats: " + JSON.stringify(stats));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get stats from a store.
|
||||||
|
getStats: function(aType, aManifestURL, aServiceType, aComponent,
|
||||||
|
aStart, aEnd, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aType + "Mgr.getStats()");
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||||
|
|
||||||
|
// Get appId and check whether manifestURL is a valid app.
|
||||||
|
let appId = 0;
|
||||||
|
if (aManifestURL) {
|
||||||
|
appId = appsService.getAppLocalIdByManifestURL(aManifestURL);
|
||||||
|
|
||||||
|
if (!appId) {
|
||||||
|
aResultCb("Invalid manifestURL", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get store name.
|
||||||
|
let storeName = statsStoreNames[aType];
|
||||||
|
|
||||||
|
// Normalize start time and end time to SAMPLE_RATE precission.
|
||||||
|
let start = this._normalizeTime(aStart, offset);
|
||||||
|
let end = this._normalizeTime(aEnd, offset);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Query time range: " + start + " to " + end);
|
||||||
|
debug("[appId, serviceType, component] = [" + appId + ", " + aServiceType
|
||||||
|
+ ", " + aComponent + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create filters.
|
||||||
|
let lowerFilter = [appId, aServiceType, aComponent, start];
|
||||||
|
let upperFilter = [appId, aServiceType, aComponent, end];
|
||||||
|
|
||||||
|
// Execute DB query.
|
||||||
|
this._dbNewTxn(storeName, "readonly", function(aTxn, aStore) {
|
||||||
|
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||||
|
|
||||||
|
let statsData = [];
|
||||||
|
|
||||||
|
if (!aTxn.result) {
|
||||||
|
aTxn.result = Object.create(null);
|
||||||
|
}
|
||||||
|
aTxn.result.type = aType;
|
||||||
|
aTxn.result.component = aComponent;
|
||||||
|
aTxn.result.serviceType = aServiceType;
|
||||||
|
aTxn.result.manifestURL = aManifestURL;
|
||||||
|
aTxn.result.start = start + offset;
|
||||||
|
aTxn.result.end = end + offset;
|
||||||
|
// Since ResourceStats() would require SAMPLE_RATE when filling the empty
|
||||||
|
// entries of statsData array, we append SAMPLE_RATE to the result field
|
||||||
|
// to save an IPC call.
|
||||||
|
aTxn.result.sampleRate = SAMPLE_RATE;
|
||||||
|
|
||||||
|
let request = aStore.openCursor(range, "prev");
|
||||||
|
if (aType == "power") {
|
||||||
|
request.onsuccess = function(aEvent) {
|
||||||
|
var cursor = aEvent.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Get " + JSON.stringify(cursor.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Covert timestamp to current timezone.
|
||||||
|
statsData.push({
|
||||||
|
timestamp: cursor.value.timestamp + offset,
|
||||||
|
consumedPower: cursor.value.consumedPower
|
||||||
|
});
|
||||||
|
cursor.continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aTxn.result.statsData = statsData;
|
||||||
|
};
|
||||||
|
} else if (aType == "network") {
|
||||||
|
request.onsuccess = function(aEvent) {
|
||||||
|
var cursor = aEvent.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Get " + JSON.stringify(cursor.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Covert timestamp to current timezone.
|
||||||
|
statsData.push({
|
||||||
|
timestamp: cursor.value.timestamp + offset,
|
||||||
|
receivedBytes: cursor.value.receivedBytes,
|
||||||
|
sentBytes: cursor.value.sentBytes
|
||||||
|
});
|
||||||
|
cursor.continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aTxn.result.statsData = statsData;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Delete the stats of a specific app/service (within a specified time range).
|
||||||
|
clearStats: function(aType, aAppId, aServiceType, aComponent,
|
||||||
|
aStart, aEnd, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aType + "Mgr.clearStats()");
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||||
|
|
||||||
|
// Get store name.
|
||||||
|
let storeName = statsStoreNames[aType];
|
||||||
|
|
||||||
|
// Normalize start and end time to SAMPLE_RATE precission.
|
||||||
|
let start = this._normalizeTime(aStart, offset);
|
||||||
|
let end = this._normalizeTime(aEnd, offset);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Query time range: " + start + " to " + end);
|
||||||
|
debug("[appId, serviceType, component] = [" + aAppId + ", " + aServiceType
|
||||||
|
+ ", " + aComponent + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create filters.
|
||||||
|
let lowerFilter = [aAppId, aServiceType, aComponent, start];
|
||||||
|
let upperFilter = [aAppId, aServiceType, aComponent, end];
|
||||||
|
|
||||||
|
// Execute clear operation.
|
||||||
|
this._dbNewTxn(storeName, "readwrite", function(aTxn, aStore) {
|
||||||
|
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||||
|
let request = aStore.openCursor(range).onsuccess = function(aEvent) {
|
||||||
|
let cursor = aEvent.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Delete " + JSON.stringify(cursor.value) + " from database");
|
||||||
|
}
|
||||||
|
cursor.delete();
|
||||||
|
cursor.continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Delete all stats saved in a store.
|
||||||
|
clearAllStats: function(aType, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aType + "Mgr.clearAllStats()");
|
||||||
|
}
|
||||||
|
|
||||||
|
let storeName = statsStoreNames[aType];
|
||||||
|
|
||||||
|
// Execute clear operation.
|
||||||
|
this._dbNewTxn(storeName, "readwrite", function(aTxn, aStore) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Clear " + aType + " stats from datastore");
|
||||||
|
}
|
||||||
|
aStore.clear();
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
addAlarm: function(aAlarm, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aAlarm.type + "Mgr.addAlarm()");
|
||||||
|
debug("alarm = " + JSON.stringify(aAlarm));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||||
|
aStore.put(aAlarm).onsuccess = function setResult(aEvent) {
|
||||||
|
// Get alarmId.
|
||||||
|
aTxn.result = aEvent.target.result;
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("New alarm ID: " + aTxn.result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert DB record to alarm object.
|
||||||
|
_recordToAlarm: function(aRecord) {
|
||||||
|
let alarm = {
|
||||||
|
alarmId: aRecord.alarmId,
|
||||||
|
type: aRecord.type,
|
||||||
|
component: aRecord.component,
|
||||||
|
serviceType: aRecord.serviceType,
|
||||||
|
manifestURL: aRecord.manifestURL,
|
||||||
|
threshold: aRecord.threshold,
|
||||||
|
data: aRecord.data
|
||||||
|
};
|
||||||
|
|
||||||
|
return alarm;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAlarms: function(aType, aOptions, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aType + "Mgr.getAlarms()");
|
||||||
|
debug("[appId, serviceType, component] = [" + aOptions.appId + ", "
|
||||||
|
+ aOptions.serviceType + ", " + aOptions.component + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute clear operation.
|
||||||
|
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||||
|
if (!aTxn.result) {
|
||||||
|
aTxn.result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let indexName = null;
|
||||||
|
let range = null;
|
||||||
|
|
||||||
|
if (aOptions) { // Get alarms associated to specified statsOptions.
|
||||||
|
indexName = "controlTarget";
|
||||||
|
range = IDBKeyRange.only([aType, aOptions.manifestURL,
|
||||||
|
aOptions.serviceType, aOptions.component]);
|
||||||
|
} else { // Get all alarms of the specified type.
|
||||||
|
indexName = "type";
|
||||||
|
range = IDBKeyRange.only(aType);
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = aStore.index(indexName).openCursor(range);
|
||||||
|
request.onsuccess = function onsuccess(aEvent) {
|
||||||
|
let cursor = aEvent.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
aTxn.result.push(this._recordToAlarm(cursor.value));
|
||||||
|
cursor.continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
}.bind(this), aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAlarm: function(aType, aAlarmId, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("removeAlarms(" + aAlarmId + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute clear operation.
|
||||||
|
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||||
|
aStore.get(aAlarmId).onsuccess = function onsuccess(aEvent) {
|
||||||
|
let alarm = aEvent.target.result;
|
||||||
|
aTxn.result = false;
|
||||||
|
if (!alarm || aType !== alarm.type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Remove alarm " + JSON.stringify(alarm) + " from datastore");
|
||||||
|
}
|
||||||
|
aStore.delete(aAlarmId);
|
||||||
|
aTxn.result = true;
|
||||||
|
};
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAllAlarms: function(aType, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aType + "Mgr.removeAllAlarms()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute clear operation.
|
||||||
|
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Remove all " + aType + " alarms from datastore.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = IDBKeyRange.only(aType);
|
||||||
|
let request = aStore.index("type").openCursor(range);
|
||||||
|
request.onsuccess = function onsuccess(aEvent) {
|
||||||
|
let cursor = aEvent.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Remove " + JSON.stringify(cursor.value) + " from database.");
|
||||||
|
}
|
||||||
|
cursor.delete();
|
||||||
|
cursor.continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get all index keys of the component.
|
||||||
|
getComponents: function(aType, aResultCb) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug(aType + "Mgr.getComponents()");
|
||||||
|
}
|
||||||
|
|
||||||
|
let storeName = statsStoreNames[aType];
|
||||||
|
|
||||||
|
this._dbNewTxn(storeName, "readonly", function(aTxn, aStore) {
|
||||||
|
if (!aTxn.result) {
|
||||||
|
aTxn.result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = aStore.index("component").openKeyCursor(null, "nextunique");
|
||||||
|
request.onsuccess = function onsuccess(aEvent) {
|
||||||
|
let cursor = aEvent.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
aTxn.result.push(cursor.key);
|
||||||
|
cursor.continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "" from the result, which indicates sum of all
|
||||||
|
// components' stats.
|
||||||
|
let index = aTxn.result.indexOf("");
|
||||||
|
if (index > -1) {
|
||||||
|
aTxn.result.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, aResultCb);
|
||||||
|
},
|
||||||
|
|
||||||
|
get sampleRate () {
|
||||||
|
return SAMPLE_RATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
get maxStorageAge() {
|
||||||
|
return MAX_STORAGE_AGE;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
480
dom/resourcestats/ResourceStatsManager.js
Normal file
480
dom/resourcestats/ResourceStatsManager.js
Normal file
@ -0,0 +1,480 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const DEBUG = false;
|
||||||
|
function debug(s) { dump("-*- ResourceStatsManager: " + s + "\n"); }
|
||||||
|
|
||||||
|
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||||
|
|
||||||
|
// Constant defines supported statistics.
|
||||||
|
const resourceTypeList = ["network", "power"];
|
||||||
|
|
||||||
|
function NetworkStatsData(aStatsData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("NetworkStatsData(): " + JSON.stringify(aStatsData));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.receivedBytes = aStatsData.receivedBytes || 0;
|
||||||
|
this.sentBytes = aStatsData.sentBytes || 0;
|
||||||
|
this.timestamp = aStatsData.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkStatsData.prototype = {
|
||||||
|
classID: Components.ID("{dce5729a-ba92-4185-8854-e29e71b9e8a2}"),
|
||||||
|
contractID: "@mozilla.org/networkStatsData;1",
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([])
|
||||||
|
};
|
||||||
|
|
||||||
|
function PowerStatsData(aStatsData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("PowerStatsData(): " + JSON.stringify(aStatsData));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.consumedPower = aStatsData.consumedPower || 0;
|
||||||
|
this.timestamp = aStatsData.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerStatsData.prototype = {
|
||||||
|
classID: Components.ID("{acb9af6c-8143-4e59-bc18-4bb1736a4004}"),
|
||||||
|
contractID: "@mozilla.org/powerStatsData;1",
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([])
|
||||||
|
};
|
||||||
|
|
||||||
|
function ResourceStats(aWindow, aStats) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("ResourceStats(): " + JSON.stringify(aStats));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._window = aWindow;
|
||||||
|
this.type = aStats.type;
|
||||||
|
this.component = aStats.component || null;
|
||||||
|
this.serviceType = aStats.serviceType || null;
|
||||||
|
this.manifestURL = aStats.manifestURL || null;
|
||||||
|
this.start = aStats.start;
|
||||||
|
this.end = aStats.end;
|
||||||
|
this.statsData = new aWindow.Array();
|
||||||
|
|
||||||
|
// A function creates a StatsData object according to type.
|
||||||
|
let createStatsDataObject = null;
|
||||||
|
let self = this;
|
||||||
|
switch (this.type) {
|
||||||
|
case "power":
|
||||||
|
createStatsDataObject = function(aStats) {
|
||||||
|
let chromeObj = new PowerStatsData(aStats);
|
||||||
|
return self._window.PowerStatsData._create(self._window, chromeObj);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "network":
|
||||||
|
createStatsDataObject = function(aStats) {
|
||||||
|
let chromeObj = new NetworkStatsData(aStats);
|
||||||
|
return self._window.NetworkStatsData._create(self._window, chromeObj);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sampleRate = aStats.sampleRate;
|
||||||
|
let queryResult = aStats.statsData;
|
||||||
|
let stats = queryResult.pop(); // Pop out the last element.
|
||||||
|
let timestamp = this.start;
|
||||||
|
|
||||||
|
// Push query result to statsData, and fill empty elements so that:
|
||||||
|
// 1. the timestamp of the first element of statsData is equal to start;
|
||||||
|
// 2. the timestamp of the last element of statsData is equal to end;
|
||||||
|
// 3. the timestamp difference of every neighboring elements is SAMPLE_RATE.
|
||||||
|
for (; timestamp <= this.end; timestamp += sampleRate) {
|
||||||
|
if (!stats || stats.timestamp != timestamp) {
|
||||||
|
// If dataArray is empty or timestamp are not equal, push a dummy object
|
||||||
|
// (which stats are set to 0) to statsData.
|
||||||
|
this.statsData.push(createStatsDataObject({ timestamp: timestamp }));
|
||||||
|
} else {
|
||||||
|
// Push stats to statsData and pop a new element form queryResult.
|
||||||
|
this.statsData.push(createStatsDataObject(stats));
|
||||||
|
stats = queryResult.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceStats.prototype = {
|
||||||
|
getData: function() {
|
||||||
|
return this.statsData;
|
||||||
|
},
|
||||||
|
|
||||||
|
classID: Components.ID("{b7c970f2-3d58-4966-9633-2024feb5132b}"),
|
||||||
|
contractID: "@mozilla.org/resourceStats;1",
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([])
|
||||||
|
};
|
||||||
|
|
||||||
|
function ResourceStatsAlarm(aWindow, aAlarm) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("ResourceStatsAlarm(): " + JSON.stringify(aAlarm));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.alarmId = aAlarm.alarmId;
|
||||||
|
this.type = aAlarm.type;
|
||||||
|
this.component = aAlarm.component || null;
|
||||||
|
this.serviceType = aAlarm.serviceType || null;
|
||||||
|
this.manifestURL = aAlarm.manifestURL || null;
|
||||||
|
this.threshold = aAlarm.threshold;
|
||||||
|
|
||||||
|
// Clone data object using structured clone algorithm.
|
||||||
|
this.data = null;
|
||||||
|
if (aAlarm.data) {
|
||||||
|
this.data = Cu.cloneInto(aAlarm.data, aWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceStatsAlarm.prototype = {
|
||||||
|
classID: Components.ID("{e2b66e7a-0ff1-4015-8690-a2a3f6a5b63a}"),
|
||||||
|
contractID: "@mozilla.org/resourceStatsAlarm;1",
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([]),
|
||||||
|
};
|
||||||
|
|
||||||
|
function ResourceStatsManager() {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("constructor()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceStatsManager.prototype = {
|
||||||
|
__proto__: DOMRequestIpcHelper.prototype,
|
||||||
|
|
||||||
|
_getPromise: function(aCallback) {
|
||||||
|
let self = this;
|
||||||
|
return this.createPromise(function(aResolve, aReject) {
|
||||||
|
let resolverId = self.getPromiseResolverId({
|
||||||
|
resolve: aResolve,
|
||||||
|
reject: aReject
|
||||||
|
});
|
||||||
|
|
||||||
|
aCallback(resolverId);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Check time range.
|
||||||
|
_checkTimeRange: function(aStart, aEnd) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("_checkTimeRange(): " + aStart + " to " + aEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = aStart ? aStart : 0;
|
||||||
|
let end = aEnd ? aEnd : Date.now();
|
||||||
|
|
||||||
|
if (start > end) {
|
||||||
|
throw Cr.NS_ERROR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { start: start, end: end };
|
||||||
|
},
|
||||||
|
|
||||||
|
getStats: function(aStatsOptions, aStart, aEnd) {
|
||||||
|
// Check time range.
|
||||||
|
let { start: start, end: end } = this._checkTimeRange(aStart, aEnd);
|
||||||
|
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:GetStats", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
statsOptions: aStatsOptions,
|
||||||
|
manifestURL: self._manifestURL,
|
||||||
|
start: start,
|
||||||
|
end: end
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearStats: function(aStatsOptions, aStart, aEnd) {
|
||||||
|
// Check time range.
|
||||||
|
let {start: start, end: end} = this._checkTimeRange(aStart, aEnd);
|
||||||
|
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:ClearStats", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
statsOptions: aStatsOptions,
|
||||||
|
manifestURL: self._manifestURL,
|
||||||
|
start: start,
|
||||||
|
end: end
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearAllStats: function() {
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:ClearAllStats", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
manifestURL: self._manifestURL
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addAlarm: function(aThreshold, aStatsOptions, aAlarmOptions) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("aStatsOptions: " + JSON.stringify(aAlarmOptions));
|
||||||
|
debug("aAlarmOptions: " + JSON.stringify(aAlarmOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse alarm options.
|
||||||
|
let startTime = aAlarmOptions.startTime || 0;
|
||||||
|
|
||||||
|
// Clone data object using structured clone algorithm.
|
||||||
|
let data = null;
|
||||||
|
if (aAlarmOptions.data) {
|
||||||
|
data = Cu.cloneInto(aAlarmOptions.data, this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:AddAlarm", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
threshold: aThreshold,
|
||||||
|
statsOptions: aStatsOptions,
|
||||||
|
manifestURL: self._manifestURL,
|
||||||
|
startTime: startTime,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getAlarms: function(aStatsOptions) {
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:GetAlarms", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
statsOptions: aStatsOptions,
|
||||||
|
manifestURL: self._manifestURL
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAlarm: function(aAlarmId) {
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:RemoveAlarm", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
manifestURL: self._manifestURL,
|
||||||
|
alarmId: aAlarmId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAllAlarms: function() {
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:RemoveAllAlarms", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
manifestURL: self._manifestURL
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getAvailableComponents: function() {
|
||||||
|
// Create Promise.
|
||||||
|
let self = this;
|
||||||
|
return this._getPromise(function(aResolverId) {
|
||||||
|
self.cpmm.sendAsyncMessage("ResourceStats:GetComponents", {
|
||||||
|
resolverId: aResolverId,
|
||||||
|
type: self.type,
|
||||||
|
manifestURL: self._manifestURL
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get resourceTypes() {
|
||||||
|
let types = new this._window.Array();
|
||||||
|
resourceTypeList.forEach(function(aType) {
|
||||||
|
types.push(aType);
|
||||||
|
});
|
||||||
|
|
||||||
|
return types;
|
||||||
|
},
|
||||||
|
|
||||||
|
get sampleRate() {
|
||||||
|
let msg = { manifestURL: this._manifestURL };
|
||||||
|
|
||||||
|
return this.cpmm.sendSyncMessage("ResourceStats:SampleRate", msg)[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
get maxStorageAge() {
|
||||||
|
let msg = { manifestURL: this._manifestURL };
|
||||||
|
|
||||||
|
return this.cpmm.sendSyncMessage("ResourceStats:MaxStorageAge", msg)[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
receiveMessage: function(aMessage) {
|
||||||
|
let data = aMessage.data;
|
||||||
|
let chromeObj = null;
|
||||||
|
let webidlObj = null;
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("receiveMessage(): " + aMessage.name + " " + data.resolverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolver = this.takePromiseResolver(data.resolverId);
|
||||||
|
if (!resolver) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (aMessage.name) {
|
||||||
|
case "ResourceStats:GetStats:Resolved":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("data.value = " + JSON.stringify(data.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
chromeObj = new ResourceStats(this._window, data.value);
|
||||||
|
webidlObj = this._window.ResourceStats._create(this._window, chromeObj);
|
||||||
|
resolver.resolve(webidlObj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ResourceStats:AddAlarm:Resolved":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("data.value = " + JSON.stringify(data.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.resolve(data.value); // data.value is alarmId.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ResourceStats:GetAlarms:Resolved":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("data.value = " + JSON.stringify(data.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
let alarmArray = this._window.Array();
|
||||||
|
data.value.forEach(function(aAlarm) {
|
||||||
|
chromeObj = new ResourceStatsAlarm(self._window, aAlarm);
|
||||||
|
webidlObj = self._window.ResourceStatsAlarm._create(self._window,
|
||||||
|
chromeObj);
|
||||||
|
alarmArray.push(webidlObj);
|
||||||
|
});
|
||||||
|
resolver.resolve(alarmArray);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ResourceStats:GetComponents:Resolved":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("data.value = " + JSON.stringify(data.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
let components = this._window.Array();
|
||||||
|
data.value.forEach(function(aComponent) {
|
||||||
|
components.push(aComponent);
|
||||||
|
});
|
||||||
|
resolver.resolve(components);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ResourceStats:ClearStats:Resolved":
|
||||||
|
case "ResourceStats:ClearAllStats:Resolved":
|
||||||
|
case "ResourceStats:RemoveAlarm:Resolved":
|
||||||
|
case "ResourceStats:RemoveAllAlarms:Resolved":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("data.value = " + JSON.stringify(data.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.resolve(data.value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ResourceStats:GetStats:Rejected":
|
||||||
|
case "ResourceStats:ClearStats:Rejected":
|
||||||
|
case "ResourceStats:ClearAllStats:Rejected":
|
||||||
|
case "ResourceStats:AddAlarm:Rejected":
|
||||||
|
case "ResourceStats:GetAlarms:Rejected":
|
||||||
|
case "ResourceStats:RemoveAlarm:Rejected":
|
||||||
|
case "ResourceStats:RemoveAllAlarms:Rejected":
|
||||||
|
case "ResourceStats:GetComponents:Rejected":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("data.reason = " + JSON.stringify(data.reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.reject(data.reason);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Could not find a handler for " + aMessage.name);
|
||||||
|
}
|
||||||
|
resolver.reject();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
__init: function(aType) {
|
||||||
|
if (resourceTypeList.indexOf(aType) < 0) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Do not support resource statistics for " + aType);
|
||||||
|
}
|
||||||
|
throw Cr.NS_ERROR_INVALID_ARG;
|
||||||
|
} else {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Create " + aType + "Mgr");
|
||||||
|
}
|
||||||
|
this.type = aType;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function(aWindow) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("init()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the manifestURL if this is an installed app
|
||||||
|
let principal = aWindow.document.nodePrincipal;
|
||||||
|
let appsService = Cc["@mozilla.org/AppsService;1"]
|
||||||
|
.getService(Ci.nsIAppsService);
|
||||||
|
this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||||
|
|
||||||
|
const messages = ["ResourceStats:GetStats:Resolved",
|
||||||
|
"ResourceStats:ClearStats:Resolved",
|
||||||
|
"ResourceStats:ClearAllStats:Resolved",
|
||||||
|
"ResourceStats:AddAlarm:Resolved",
|
||||||
|
"ResourceStats:GetAlarms:Resolved",
|
||||||
|
"ResourceStats:RemoveAlarm:Resolved",
|
||||||
|
"ResourceStats:RemoveAllAlarms:Resolved",
|
||||||
|
"ResourceStats:GetComponents:Resolved",
|
||||||
|
"ResourceStats:GetStats:Rejected",
|
||||||
|
"ResourceStats:ClearStats:Rejected",
|
||||||
|
"ResourceStats:ClearAllStats:Rejected",
|
||||||
|
"ResourceStats:AddAlarm:Rejected",
|
||||||
|
"ResourceStats:GetAlarms:Rejected",
|
||||||
|
"ResourceStats:RemoveAlarm:Rejected",
|
||||||
|
"ResourceStats:RemoveAllAlarms:Rejected",
|
||||||
|
"ResourceStats:GetComponents:Rejected"];
|
||||||
|
this.initDOMRequestHelper(aWindow, messages);
|
||||||
|
|
||||||
|
this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||||
|
.getService(Ci.nsISyncMessageSender);
|
||||||
|
},
|
||||||
|
|
||||||
|
classID: Components.ID("{101ed1f8-31b3-491c-95ea-04091e6e8027}"),
|
||||||
|
contractID: "@mozilla.org/resourceStatsManager;1",
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||||
|
Ci.nsISupportsWeakReference,
|
||||||
|
Ci.nsIObserver])
|
||||||
|
};
|
||||||
|
|
||||||
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
|
||||||
|
PowerStatsData,
|
||||||
|
ResourceStats,
|
||||||
|
ResourceStatsAlarm,
|
||||||
|
ResourceStatsManager]);
|
||||||
|
|
14
dom/resourcestats/ResourceStatsManager.manifest
Normal file
14
dom/resourcestats/ResourceStatsManager.manifest
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
component {dce5729a-ba92-4185-8854-e29e71b9e8a2} ResourceStatsManager.js
|
||||||
|
contract @mozilla.org/networkStatsData;1 {dce5729a-ba92-4185-8854-e29e71b9e8a2}
|
||||||
|
|
||||||
|
component {acb9af6c-8143-4e59-bc18-4bb1736a4004} ResourceStatsManager.js
|
||||||
|
contract @mozilla.org/powerStatsData;1 {acb9af6c-8143-4e59-bc18-4bb1736a4004}
|
||||||
|
|
||||||
|
component {b7c970f2-3d58-4966-9633-2024feb5132b} ResourceStatsManager.js
|
||||||
|
contract @mozilla.org/resourceStats;1 {b7c970f2-3d58-4966-9633-2024feb5132b}
|
||||||
|
|
||||||
|
component {e2b66e7a-0ff1-4015-8690-a2a3f6a5b63a} ResourceStatsManager.js
|
||||||
|
contract @mozilla.org/resourceStatsAlarm;1 {e2b66e7a-0ff1-4015-8690-a2a3f6a5b63a}
|
||||||
|
|
||||||
|
component {101ed1f8-31b3-491c-95ea-04091e6e8027} ResourceStatsManager.js
|
||||||
|
contract @mozilla.org/resourceStatsManager;1 {101ed1f8-31b3-491c-95ea-04091e6e8027}
|
334
dom/resourcestats/ResourceStatsService.jsm
Normal file
334
dom/resourcestats/ResourceStatsService.jsm
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["ResourceStatsService"];
|
||||||
|
|
||||||
|
const DEBUG = false;
|
||||||
|
function debug(s) { dump("-*- ResourceStatsService: " + s + "\n"); }
|
||||||
|
|
||||||
|
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
// Load ResourceStatsDB.
|
||||||
|
Cu.import("resource://gre/modules/ResourceStatsDB.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
|
||||||
|
"@mozilla.org/dom/indexeddb/manager;1",
|
||||||
|
"nsIIndexedDatabaseManager");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||||
|
"@mozilla.org/parentprocessmessagemanager;1",
|
||||||
|
"nsIMessageListenerManager");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||||
|
"@mozilla.org/AppsService;1",
|
||||||
|
"nsIAppsService");
|
||||||
|
|
||||||
|
this.ResourceStatsService = {
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Service started");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set notification to observe.
|
||||||
|
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||||
|
|
||||||
|
// Add message listener.
|
||||||
|
this.messages = ["ResourceStats:GetStats",
|
||||||
|
"ResourceStats:ClearStats",
|
||||||
|
"ResourceStats:ClearAllStats",
|
||||||
|
"ResourceStats:AddAlarm",
|
||||||
|
"ResourceStats:GetAlarms",
|
||||||
|
"ResourceStats:RemoveAlarm",
|
||||||
|
"ResourceStats:RemoveAllAlarms",
|
||||||
|
"ResourceStats:GetComponents",
|
||||||
|
"ResourceStats:SampleRate",
|
||||||
|
"ResourceStats:MaxStorageAge"];
|
||||||
|
|
||||||
|
this.messages.forEach(function(aMessageName){
|
||||||
|
ppmm.addMessageListener(aMessageName, this);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
// Create indexedDB.
|
||||||
|
this._db = new ResourceStatsDB();
|
||||||
|
},
|
||||||
|
|
||||||
|
receiveMessage: function(aMessage) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("receiveMessage(): " + aMessage.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mm = aMessage.target;
|
||||||
|
let data = aMessage.data;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("received aMessage.data = " + JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// To prevent the hacked child process from sending commands to parent,
|
||||||
|
// we need to check its permission and manifest URL.
|
||||||
|
if (!aMessage.target.assertPermission("resourcestats-manage")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!aMessage.target.assertContainApp(data.manifestURL)) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Got msg from a child process containing illegal manifestURL.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (aMessage.name) {
|
||||||
|
case "ResourceStats:GetStats":
|
||||||
|
this.getStats(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:ClearStats":
|
||||||
|
this.clearStats(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:ClearAllStats":
|
||||||
|
this.clearAllStats(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:AddAlarm":
|
||||||
|
this.addAlarm(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:GetAlarms":
|
||||||
|
this.getAlarms(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:RemoveAlarm":
|
||||||
|
this.removeAlarm(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:RemoveAllAlarms":
|
||||||
|
this.removeAllAlarms(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:GetComponents":
|
||||||
|
this.getComponents(mm, data);
|
||||||
|
break;
|
||||||
|
case "ResourceStats:SampleRate":
|
||||||
|
// This message is sync.
|
||||||
|
return this._db.sampleRate;
|
||||||
|
case "ResourceStats:MaxStorageAge":
|
||||||
|
// This message is sync.
|
||||||
|
return this._db.maxStorageAge;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
observe: function(aSubject, aTopic, aData) {
|
||||||
|
switch (aTopic) {
|
||||||
|
case "xpcom-shutdown":
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("Service shutdown " + aData);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages.forEach(function(aMessageName) {
|
||||||
|
ppmm.removeMessageListener(aMessageName, this);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Closure generates callback function for DB request.
|
||||||
|
_createDbCallback: function(aMm, aId, aMessage) {
|
||||||
|
let resolveMsg = aMessage + ":Resolved";
|
||||||
|
let rejectMsg = aMessage + ":Rejected";
|
||||||
|
|
||||||
|
return function(aError, aResult) {
|
||||||
|
if (aError) {
|
||||||
|
aMm.sendAsyncMessage(rejectMsg, { resolverId: aId, reason: aError });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aMm.sendAsyncMessage(resolveMsg, { resolverId: aId, value: aResult });
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getStats: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("getStats(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we validate the manifestURL in _db.getStats().
|
||||||
|
let manifestURL = aData.statsOptions.manifestURL || "";
|
||||||
|
let serviceType = aData.statsOptions.serviceType || "";
|
||||||
|
let component = aData.statsOptions.component || "";
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onStatsGot = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:GetStats");
|
||||||
|
this._db.getStats(aData.type, manifestURL, serviceType, component,
|
||||||
|
aData.start, aData.end, onStatsGot);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearStats: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("clearStats(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get appId and check whether manifestURL is a valid app.
|
||||||
|
let appId = 0;
|
||||||
|
let manifestURL = aData.statsOptions.manifestURL || "";
|
||||||
|
if (manifestURL) {
|
||||||
|
appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||||
|
|
||||||
|
if (!appId) {
|
||||||
|
aMm.sendAsyncMessage("ResourceStats:GetStats:Rejected", {
|
||||||
|
resolverId: aData.resolverId,
|
||||||
|
reason: "Invalid manifestURL"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let serviceType = aData.statsOptions.serviceType || "";
|
||||||
|
let component = aData.statsOptions.component || "";
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onStatsCleared = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:ClearStats");
|
||||||
|
this._db.clearStats(aData.type, appId, serviceType, component,
|
||||||
|
aData.start, aData.end, onStatsCleared);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearAllStats: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("clearAllStats(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onAllStatsCleared = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:ClearAllStats");
|
||||||
|
this._db.clearAllStats(aData.type, onAllStatsCleared);
|
||||||
|
},
|
||||||
|
|
||||||
|
addAlarm: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("addAlarm(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get appId and check whether manifestURL is a valid app.
|
||||||
|
let manifestURL = aData.statsOptions.manifestURL;
|
||||||
|
if (manifestURL) {
|
||||||
|
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||||
|
|
||||||
|
if (!appId) {
|
||||||
|
aMm.sendAsyncMessage("ResourceStats:GetStats:Rejected", {
|
||||||
|
resolverId: aData.resolverId,
|
||||||
|
reason: "Invalid manifestURL"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an alarm object.
|
||||||
|
let newAlarm = {
|
||||||
|
type: aData.type,
|
||||||
|
component: aData.statsOptions.component || "",
|
||||||
|
serviceType: aData.statsOptions.serviceType || "",
|
||||||
|
manifestURL: manifestURL || "",
|
||||||
|
threshold: aData.threshold,
|
||||||
|
startTime: aData.startTime,
|
||||||
|
data: aData.data
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onAlarmAdded = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:AddAlarm");
|
||||||
|
this._db.addAlarm(newAlarm, onAlarmAdded);
|
||||||
|
},
|
||||||
|
|
||||||
|
getAlarms: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("getAlarms(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = null;
|
||||||
|
let statsOptions = aData.statsOptions;
|
||||||
|
// If all keys in statsOptions are set to null, treat this call as quering
|
||||||
|
// all alarms; otherwise, resolve the statsOptions and perform DB query
|
||||||
|
// according to the resolved result.
|
||||||
|
if (statsOptions.manifestURL || statsOptions.serviceType ||
|
||||||
|
statsOptions.component) {
|
||||||
|
// Validate manifestURL.
|
||||||
|
let manifestURL = statsOptions.manifestURL || "";
|
||||||
|
if (manifestURL) {
|
||||||
|
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||||
|
|
||||||
|
if (!appId) {
|
||||||
|
aMm.sendAsyncMessage("ResourceStats:GetStats:Rejected", {
|
||||||
|
resolverId: aData.resolverId,
|
||||||
|
reason: "Invalid manifestURL"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options = {
|
||||||
|
manifestURL: manifestURL,
|
||||||
|
serviceType: statsOptions.serviceType || "",
|
||||||
|
component: statsOptions.component || ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onAlarmsGot = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:GetAlarms");
|
||||||
|
this._db.getAlarms(aData.type, options, onAlarmsGot);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAlarm: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("removeAlarm(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onAlarmRemoved = function(aError, aResult) {
|
||||||
|
if (aError) {
|
||||||
|
aMm.sendAsyncMessage("ResourceStats:RemoveAlarm:Rejected",
|
||||||
|
{ resolverId: aData.resolverId, reason: aError });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aResult) {
|
||||||
|
aMm.sendAsyncMessage("ResourceStats:RemoveAlarm:Rejected",
|
||||||
|
{ resolverId: aData.resolverId,
|
||||||
|
reason: "alarm not existed" });
|
||||||
|
}
|
||||||
|
|
||||||
|
aMm.sendAsyncMessage("ResourceStats:RemoveAlarm:Resolved",
|
||||||
|
{ resolverId: aData.resolverId, value: aResult });
|
||||||
|
};
|
||||||
|
|
||||||
|
this._db.removeAlarm(aData.type, aData.alarmId, onAlarmRemoved);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAllAlarms: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("removeAllAlarms(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onAllAlarmsRemoved = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:RemoveAllAlarms");
|
||||||
|
this._db.removeAllAlarms(aData.type, onAllAlarmsRemoved);
|
||||||
|
},
|
||||||
|
|
||||||
|
getComponents: function(aMm, aData) {
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("getComponents(): " + JSON.stringify(aData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute DB operation.
|
||||||
|
let onComponentsGot = this._createDbCallback(aMm, aData.resolverId,
|
||||||
|
"ResourceStats:GetComponents");
|
||||||
|
this._db.getComponents(aData.type, onComponentsGot);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ResourceStatsService.init();
|
||||||
|
|
26
dom/resourcestats/moz.build
Normal file
26
dom/resourcestats/moz.build
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# vim: set filetype=python:
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
EXPORTS.mozilla.dom += [
|
||||||
|
'ResourceStatsControl.h',
|
||||||
|
]
|
||||||
|
|
||||||
|
SOURCES += [
|
||||||
|
'ResourceStatsControl.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
EXTRA_COMPONENTS += [
|
||||||
|
'ResourceStatsManager.js',
|
||||||
|
'ResourceStatsManager.manifest',
|
||||||
|
]
|
||||||
|
|
||||||
|
EXTRA_JS_MODULES += [
|
||||||
|
'ResourceStatsDB.jsm',
|
||||||
|
'ResourceStatsService.jsm',
|
||||||
|
]
|
||||||
|
|
||||||
|
FINAL_LIBRARY = 'gklayout'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user