зеркало из https://github.com/mozilla/gecko-dev.git
Bug 928575 - Overhaul FHR data collection for extensions and plugins for desktop. r=gps
This commit is contained in:
Родитель
e6716f214c
Коммит
84585a7828
|
@ -537,11 +537,18 @@ version number is incremented.
|
|||
|
||||
All measurements are defined alphabetically in the sections below.
|
||||
|
||||
org.mozilla.addons.active
|
||||
org.mozilla.addons.addons
|
||||
-------------------------
|
||||
|
||||
This measurement contains information about the currently-installed add-ons.
|
||||
|
||||
Version 2
|
||||
^^^^^^^^^
|
||||
|
||||
This version adds the human-readable fields *name* and *description*, both
|
||||
coming directly from the Addon instance as most properties in version 1.
|
||||
Also, all plugin details are now in org.mozilla.addons.plugins.
|
||||
|
||||
Version 1
|
||||
^^^^^^^^^
|
||||
|
||||
|
@ -576,32 +583,99 @@ Example
|
|||
^^^^^^^
|
||||
::
|
||||
|
||||
"org.mozilla.addons.active": {
|
||||
"_v": 1,
|
||||
"SQLiteManager@mrinalkant.blogspot.com": {
|
||||
"org.mozilla.addons.addons": {
|
||||
"_v": 2,
|
||||
"{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}": {
|
||||
"userDisabled": false,
|
||||
"appDisabled": false,
|
||||
"version": "0.7.7",
|
||||
"name": "Adblock Plus",
|
||||
"version": "2.4.1",
|
||||
"type": "extension",
|
||||
"scope": 1,
|
||||
"description": "Ads were yesterday!",
|
||||
"foreignInstall": false,
|
||||
"hasBinaryComponents": false,
|
||||
"installDay": 15196,
|
||||
"updateDay": 15307
|
||||
"installDay": 16093,
|
||||
"updateDay": 16093
|
||||
},
|
||||
"testpilot@labs.mozilla.com": {
|
||||
"userDisabled": false,
|
||||
"{e4a8a97b-f2ed-450b-b12d-ee082ba24781}": {
|
||||
"userDisabled": true,
|
||||
"appDisabled": false,
|
||||
"version": "1.2.2",
|
||||
"name": "Greasemonkey",
|
||||
"version": "1.14",
|
||||
"type": "extension",
|
||||
"scope": 1,
|
||||
"description": "A User Script Manager for Firefox",
|
||||
"foreignInstall": false,
|
||||
"hasBinaryComponents": false,
|
||||
"installDay": 15176,
|
||||
"updateDay": 15595
|
||||
"installDay": 16093,
|
||||
"updateDay": 16093
|
||||
}
|
||||
}
|
||||
|
||||
org.mozilla.addons.plugins
|
||||
-------------------------
|
||||
|
||||
This measurement contains information about the currently-installed plugins.
|
||||
|
||||
Version 1
|
||||
^^^^^^^^^
|
||||
|
||||
The measurement object is a mapping of plugin IDs to objects containing
|
||||
plugin metadata.
|
||||
|
||||
The plugin ID is constructed of the plugins filename, name, version and
|
||||
description. Every plugin has at least a filename and a name.
|
||||
|
||||
Each plugin contains the following properties:
|
||||
|
||||
* name
|
||||
* version
|
||||
* description
|
||||
* blocklisted
|
||||
* disabled
|
||||
* clicktoplay
|
||||
* mimeTypes
|
||||
* updateDay
|
||||
|
||||
With the exception of *updateDay* and *mimeTypes*, all these properties come
|
||||
directly from ``nsIPluginTag`` via ``nsIPluginHost``.
|
||||
*updateDay* is the number of days since UNIX epoch of the plugins last modified
|
||||
time.
|
||||
*mimeTypes* is the list of mimetypes the plugin supports, see
|
||||
``nsIPluginTag.getMimeTypes()`.
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
"org.mozilla.addons.plugins": {
|
||||
"_v": 1,
|
||||
"Flash Player.plugin:Shockwave Flash:12.0.0.38:Shockwave Flash 12.0 r0": {
|
||||
"mimeTypes": [
|
||||
"application/x-shockwave-flash",
|
||||
"application/futuresplash"
|
||||
],
|
||||
"name": "Shockwave Flash",
|
||||
"version": "12.0.0.38",
|
||||
"description": "Shockwave Flash 12.0 r0",
|
||||
"blocklisted": false,
|
||||
"disabled": false,
|
||||
"clicktoplay": false
|
||||
},
|
||||
"Default Browser.plugin:Default Browser Helper:537:Provides information about the default web browser": {
|
||||
"mimeTypes": [
|
||||
"application/apple-default-browser"
|
||||
],
|
||||
"name": "Default Browser Helper",
|
||||
"version": "537",
|
||||
"description": "Provides information about the default web browser",
|
||||
"blocklisted": false,
|
||||
"disabled": true,
|
||||
"clicktoplay": false
|
||||
}
|
||||
}
|
||||
|
||||
org.mozilla.addons.counts
|
||||
-------------------------
|
||||
|
|
|
@ -696,8 +696,8 @@ function ActiveAddonsMeasurement() {
|
|||
ActiveAddonsMeasurement.prototype = Object.freeze({
|
||||
__proto__: Metrics.Measurement.prototype,
|
||||
|
||||
name: "active",
|
||||
version: 1,
|
||||
name: "addons",
|
||||
version: 2,
|
||||
|
||||
fields: {
|
||||
addons: LAST_TEXT_FIELD,
|
||||
|
@ -705,7 +705,7 @@ ActiveAddonsMeasurement.prototype = Object.freeze({
|
|||
|
||||
_serializeJSONSingular: function (data) {
|
||||
if (!data.has("addons")) {
|
||||
this._log.warn("Don't have active addons info. Weird.");
|
||||
this._log.warn("Don't have addons info. Weird.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -716,6 +716,45 @@ ActiveAddonsMeasurement.prototype = Object.freeze({
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Stores the set of active plugins in storage.
|
||||
*
|
||||
* This stores the data in a JSON blob in a text field similar to the
|
||||
* ActiveAddonsMeasurement.
|
||||
*/
|
||||
function ActivePluginsMeasurement() {
|
||||
Metrics.Measurement.call(this);
|
||||
|
||||
this._serializers = {};
|
||||
this._serializers[this.SERIALIZE_JSON] = {
|
||||
singular: this._serializeJSONSingular.bind(this),
|
||||
// We don't need a daily serializer because we have none of this data.
|
||||
};
|
||||
}
|
||||
|
||||
ActivePluginsMeasurement.prototype = Object.freeze({
|
||||
__proto__: Metrics.Measurement.prototype,
|
||||
|
||||
name: "plugins",
|
||||
version: 1,
|
||||
|
||||
fields: {
|
||||
plugins: LAST_TEXT_FIELD,
|
||||
},
|
||||
|
||||
_serializeJSONSingular: function (data) {
|
||||
if (!data.has("plugins")) {
|
||||
this._log.warn("Don't have plugins info. Weird.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Exceptions are caught in the caller.
|
||||
let result = JSON.parse(data.get("plugins")[1]);
|
||||
result._v = this.version;
|
||||
return result;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
function AddonCountsMeasurement() {
|
||||
Metrics.Measurement.call(this);
|
||||
|
@ -783,7 +822,6 @@ AddonsProvider.prototype = Object.freeze({
|
|||
// Add-on types for which full details are uploaded in the
|
||||
// ActiveAddonsMeasurement. All other types are ignored.
|
||||
FULL_DETAIL_TYPES: [
|
||||
"plugin",
|
||||
"extension",
|
||||
"service",
|
||||
],
|
||||
|
@ -792,6 +830,7 @@ AddonsProvider.prototype = Object.freeze({
|
|||
|
||||
measurementTypes: [
|
||||
ActiveAddonsMeasurement,
|
||||
ActivePluginsMeasurement,
|
||||
AddonCountsMeasurement1,
|
||||
AddonCountsMeasurement,
|
||||
],
|
||||
|
@ -826,9 +865,11 @@ AddonsProvider.prototype = Object.freeze({
|
|||
AddonManager.getAllAddons(function onAllAddons(addons) {
|
||||
let data;
|
||||
let addonsField;
|
||||
let pluginsField;
|
||||
try {
|
||||
data = this._createDataStructure(addons);
|
||||
addonsField = JSON.stringify(data.addons);
|
||||
pluginsField = JSON.stringify(data.plugins);
|
||||
} catch (ex) {
|
||||
this._log.warn("Exception when populating add-ons data structure: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
|
@ -837,7 +878,8 @@ AddonsProvider.prototype = Object.freeze({
|
|||
}
|
||||
|
||||
let now = new Date();
|
||||
let active = this.getMeasurement("active", 1);
|
||||
let addons = this.getMeasurement("addons", 2);
|
||||
let plugins = this.getMeasurement("plugins", 1);
|
||||
let counts = this.getMeasurement(AddonCountsMeasurement.prototype.name,
|
||||
AddonCountsMeasurement.prototype.version);
|
||||
|
||||
|
@ -853,8 +895,13 @@ AddonsProvider.prototype = Object.freeze({
|
|||
counts.setDailyLastNumeric(type, data.counts[type], now);
|
||||
}
|
||||
|
||||
return active.setLastText("addons", addonsField).then(
|
||||
function onSuccess() { deferred.resolve(); },
|
||||
return addons.setLastText("addons", addonsField).then(
|
||||
function onSuccess() {
|
||||
return plugins.setLastText("plugins", pluginsField).then(
|
||||
function onSuccess() { deferred.resolve(); },
|
||||
function onError(error) { deferred.reject(error); }
|
||||
);
|
||||
},
|
||||
function onError(error) { deferred.reject(error); }
|
||||
);
|
||||
}.bind(this));
|
||||
|
@ -863,21 +910,41 @@ AddonsProvider.prototype = Object.freeze({
|
|||
return deferred.promise;
|
||||
},
|
||||
|
||||
COPY_FIELDS: [
|
||||
COPY_ADDON_FIELDS: [
|
||||
"userDisabled",
|
||||
"appDisabled",
|
||||
"name",
|
||||
"version",
|
||||
"type",
|
||||
"scope",
|
||||
"description",
|
||||
"foreignInstall",
|
||||
"hasBinaryComponents",
|
||||
],
|
||||
|
||||
COPY_PLUGIN_FIELDS: [
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"blocklisted",
|
||||
"disabled",
|
||||
"clicktoplay",
|
||||
],
|
||||
|
||||
_createDataStructure: function (addons) {
|
||||
let data = {addons: {}, counts: {}};
|
||||
let data = {
|
||||
addons: {},
|
||||
plugins: {},
|
||||
counts: {}
|
||||
};
|
||||
|
||||
for (let addon of addons) {
|
||||
let type = addon.type;
|
||||
|
||||
// We count plugins separately below.
|
||||
if (addon.type == "plugin")
|
||||
continue;
|
||||
|
||||
data.counts[type] = (data.counts[type] || 0) + 1;
|
||||
|
||||
if (this.FULL_DETAIL_TYPES.indexOf(addon.type) == -1) {
|
||||
|
@ -885,7 +952,7 @@ AddonsProvider.prototype = Object.freeze({
|
|||
}
|
||||
|
||||
let obj = {};
|
||||
for (let field of this.COPY_FIELDS) {
|
||||
for (let field of this.COPY_ADDON_FIELDS) {
|
||||
obj[field] = addon[field];
|
||||
}
|
||||
|
||||
|
@ -898,9 +965,29 @@ AddonsProvider.prototype = Object.freeze({
|
|||
}
|
||||
|
||||
data.addons[addon.id] = obj;
|
||||
|
||||
}
|
||||
|
||||
let pluginTags = Cc["@mozilla.org/plugin/host;1"].
|
||||
getService(Ci.nsIPluginHost).
|
||||
getPluginTags({});
|
||||
|
||||
for (let tag of pluginTags) {
|
||||
let obj = {
|
||||
mimeTypes: tag.getMimeTypes({}),
|
||||
};
|
||||
|
||||
for (let field of this.COPY_PLUGIN_FIELDS) {
|
||||
obj[field] = tag[field];
|
||||
}
|
||||
|
||||
// Plugins need to have a filename and a name, so this can't be empty.
|
||||
let id = tag.filename + ":" + tag.name + ":" + tag.version + ":"
|
||||
+ tag.description;
|
||||
data.plugins[id] = obj;
|
||||
}
|
||||
|
||||
data.counts["plugin"] = pluginTags.length;
|
||||
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
const {utils: Cu, classes: Cc, interfaces: Ci} = Components;
|
||||
|
||||
|
||||
Cu.import("resource://gre/modules/Metrics.jsm");
|
||||
|
@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/services/healthreport/providers.jsm");
|
|||
let gGlobalScope = this;
|
||||
function loadAddonManager() {
|
||||
let ns = {};
|
||||
Components.utils.import("resource://gre/modules/Services.jsm", ns);
|
||||
Cu.import("resource://gre/modules/Services.jsm", ns);
|
||||
let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
|
||||
let file = do_get_file(head);
|
||||
let uri = ns.Services.io.newFileURI(file);
|
||||
|
@ -64,7 +64,7 @@ add_task(function test_collect() {
|
|||
let now = new Date();
|
||||
|
||||
// FUTURE install add-on via AddonManager and don't use monkeypatching.
|
||||
let addons = [
|
||||
let testAddons = [
|
||||
{
|
||||
id: "addon0",
|
||||
userDisabled: false,
|
||||
|
@ -77,6 +77,7 @@ add_task(function test_collect() {
|
|||
installDate: now,
|
||||
updateDate: now,
|
||||
},
|
||||
// This plugin entry should get ignored.
|
||||
{
|
||||
id: "addon1",
|
||||
userDisabled: false,
|
||||
|
@ -113,15 +114,54 @@ add_task(function test_collect() {
|
|||
hasBinaryComponents: false,
|
||||
installDate: now,
|
||||
updateDate: now,
|
||||
description: "addon3 description"
|
||||
},
|
||||
];
|
||||
|
||||
monkeypatchAddons(provider, addons);
|
||||
monkeypatchAddons(provider, testAddons);
|
||||
|
||||
let testPlugins = {
|
||||
"Test Plug-in":
|
||||
{
|
||||
"version": "1.0.0.0",
|
||||
"description": "Plug-in for testing purposes.™ (हिन्दी 中文 العربية)",
|
||||
"blocklisted": false,
|
||||
"disabled": false,
|
||||
"clicktoplay": false,
|
||||
"mimeTypes":[
|
||||
"application/x-test"
|
||||
],
|
||||
},
|
||||
"Second Test Plug-in":
|
||||
{
|
||||
"version": "1.0.0.0",
|
||||
"description": "Second plug-in for testing purposes.",
|
||||
"blocklisted": false,
|
||||
"disabled": false,
|
||||
"clicktoplay": false,
|
||||
"mimeTypes":[
|
||||
"application/x-second-test"
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
let pluginTags = Cc["@mozilla.org/plugin/host;1"]
|
||||
.getService(Ci.nsIPluginHost)
|
||||
.getPluginTags({});
|
||||
|
||||
for (let tag of pluginTags) {
|
||||
if (tag.name in testPlugins) {
|
||||
let p = testPlugins[tag.name];
|
||||
p.id = tag.filename+":"+tag.name+":"+p.version+":"+p.description;
|
||||
}
|
||||
}
|
||||
|
||||
yield provider.collectConstantData();
|
||||
|
||||
let active = provider.getMeasurement("active", 1);
|
||||
let data = yield active.getValues();
|
||||
// Test addons measurement.
|
||||
|
||||
let addons = provider.getMeasurement("addons", 2);
|
||||
let data = yield addons.getValues();
|
||||
|
||||
do_check_eq(data.days.size, 0);
|
||||
do_check_eq(data.singular.size, 1);
|
||||
|
@ -130,20 +170,63 @@ add_task(function test_collect() {
|
|||
let json = data.singular.get("addons")[1];
|
||||
let value = JSON.parse(json);
|
||||
do_check_eq(typeof(value), "object");
|
||||
do_check_eq(Object.keys(value).length, 3);
|
||||
do_check_eq(Object.keys(value).length, 2);
|
||||
do_check_true("addon0" in value);
|
||||
do_check_true("addon1" in value);
|
||||
do_check_true(!("addon1" in value));
|
||||
do_check_true(!("addon2" in value));
|
||||
do_check_true("addon3" in value);
|
||||
|
||||
let serializer = active.serializer(active.SERIALIZE_JSON);
|
||||
let serializer = addons.serializer(addons.SERIALIZE_JSON);
|
||||
let serialized = serializer.singular(data.singular);
|
||||
do_check_eq(typeof(serialized), "object");
|
||||
do_check_eq(Object.keys(serialized).length, 4); // Our three keys, plus _v.
|
||||
do_check_eq(Object.keys(serialized).length, 3); // Our entries, plus _v.
|
||||
do_check_true("addon0" in serialized);
|
||||
do_check_true("addon1" in serialized);
|
||||
do_check_true("addon3" in serialized);
|
||||
do_check_eq(serialized._v, 2);
|
||||
|
||||
// Test plugins measurement.
|
||||
|
||||
let plugins = provider.getMeasurement("plugins", 1);
|
||||
data = yield plugins.getValues();
|
||||
|
||||
do_check_eq(data.days.size, 0);
|
||||
do_check_eq(data.singular.size, 1);
|
||||
do_check_true(data.singular.has("plugins"));
|
||||
|
||||
json = data.singular.get("plugins")[1];
|
||||
value = JSON.parse(json);
|
||||
do_check_eq(typeof(value), "object");
|
||||
do_check_eq(Object.keys(value).length, 2);
|
||||
|
||||
do_check_true(testPlugins["Test Plug-in"].id in value);
|
||||
do_check_true(testPlugins["Second Test Plug-in"].id in value);
|
||||
|
||||
for (let id in value) {
|
||||
let item = value[id];
|
||||
let testData = testPlugins[item.name];
|
||||
for (let prop in testData) {
|
||||
if (prop == "mimeTypes" || prop == "id") {
|
||||
continue;
|
||||
}
|
||||
do_check_eq(testData[prop], item[prop]);
|
||||
}
|
||||
|
||||
for (let mime of testData.mimeTypes) {
|
||||
do_check_true(item.mimeTypes.indexOf(mime) != -1);
|
||||
}
|
||||
}
|
||||
|
||||
serializer = plugins.serializer(plugins.SERIALIZE_JSON);
|
||||
serialized = serializer.singular(data.singular);
|
||||
do_check_eq(typeof(serialized), "object");
|
||||
do_check_eq(Object.keys(serialized).length, 3); // Our entries, plus _v.
|
||||
for (let name in testPlugins) {
|
||||
do_check_true(testPlugins[name].id in serialized);
|
||||
}
|
||||
do_check_eq(serialized._v, 1);
|
||||
|
||||
// Test counts measurement.
|
||||
|
||||
let counts = provider.getMeasurement("counts", 2);
|
||||
data = yield counts.getValues();
|
||||
do_check_eq(data.days.size, 1);
|
||||
|
@ -153,7 +236,7 @@ add_task(function test_collect() {
|
|||
value = data.days.getDay(now);
|
||||
do_check_eq(value.size, 4);
|
||||
do_check_eq(value.get("extension"), 1);
|
||||
do_check_eq(value.get("plugin"), 1);
|
||||
do_check_eq(value.get("plugin"), 2);
|
||||
do_check_eq(value.get("theme"), 1);
|
||||
do_check_eq(value.get("service"), 1);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче