Bug 1454378 - cache and inline things, avoid duplicate attribute or property requests, to make blocklist faster, r=florian

MozReview-Commit-ID: BwBhZr6sqx2

--HG--
extra : rebase_source : e5cf8d9b51118730701fe7b09befc006413b33aa
This commit is contained in:
Gijs Kruitbosch 2018-06-13 17:06:49 -07:00
Родитель d0c0e7fcc2
Коммит 9f327f0d76
3 изменённых файлов: 120 добавлений и 106 удалений

Просмотреть файл

@ -181,6 +181,14 @@ XPCOMUtils.defineLazyGetter(this, "gApp", function() {
return appinfo;
});
XPCOMUtils.defineLazyGetter(this, "gAppID", function() {
return gApp.ID;
});
XPCOMUtils.defineLazyGetter(this, "gAppVersion", function() {
return gApp.version;
});
XPCOMUtils.defineLazyGetter(this, "gABI", function() {
let abi = null;
try {
@ -248,14 +256,16 @@ function restartApp() {
* Whether the entry matches the current OS.
*/
function matchesOSABI(blocklistElement) {
if (blocklistElement.hasAttribute("os")) {
var choices = blocklistElement.getAttribute("os").split(",");
let os = blocklistElement.getAttribute("os");
if (os) {
let choices = os.split(",");
if (choices.length > 0 && !choices.includes(gApp.OS))
return false;
}
if (blocklistElement.hasAttribute("xpcomabi")) {
choices = blocklistElement.getAttribute("xpcomabi").split(",");
let xpcomabi = blocklistElement.getAttribute("xpcomabi");
if (xpcomabi) {
let choices = xpcomabi.split(",");
if (choices.length > 0 && !choices.includes(gApp.XPCOMABI))
return false;
}
@ -279,22 +289,6 @@ function getDistributionPrefValue(aPrefName) {
return Services.prefs.getDefaultBranch(null).getCharPref(aPrefName, "default");
}
/**
* Parse a string representation of a regular expression. Needed because we
* use the /pattern/flags form (because it's detectable), which is only
* supported as a literal in JS.
*
* @param {string} aStr
* String representation of regexp
* @return {RegExp} instance
*/
function parseRegExp(aStr) {
let lastSlash = aStr.lastIndexOf("/");
let pattern = aStr.slice(1, lastSlash);
let flags = aStr.slice(lastSlash + 1);
return new RegExp(pattern, flags);
}
/**
* Manages the Blocklist. The Blocklist is a representation of the contents of
* blocklist.xml and allows us to remotely disable / re-enable blocklisted
@ -441,11 +435,11 @@ var Blocklist = {
return null;
// Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
if (!appVersion && !gApp.version)
if (!appVersion && !gAppVersion)
return null;
if (!appVersion)
appVersion = gApp.version;
appVersion = gAppVersion;
if (!toolkitVersion)
toolkitVersion = gApp.platformVersion;
@ -536,7 +530,7 @@ var Blocklist = {
// (For every non-null property in entry, the same key must exist in
// params and value must be the same)
function checkEntry(entry, params) {
for (let [key, value] of entry) {
for (let [key, value] of Object.entries(entry)) {
if (value === null || value === undefined)
continue;
if (params[key]) {
@ -612,27 +606,33 @@ var Blocklist = {
if (pingCountTotal < 1)
pingCountTotal = 1;
dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
// Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
if (gApp.version)
dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
// Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
if (gApp.version)
dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
dsURI = dsURI.replace(/%CHANNEL%/g, UpdateUtils.UpdateChannel);
dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
dsURI = dsURI.replace(/%DISTRIBUTION%/g,
getDistributionPrefValue(PREF_APP_DISTRIBUTION));
dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion);
dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal);
dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
let replacements = {
APP_ID: gAppID,
PRODUCT: gApp.name,
BUILD_ID: gApp.appBuildID,
BUILD_TARGET: gApp.OS + "_" + gABI,
OS_VERSION: gOSVersion,
LOCALE: getLocale(),
CHANNEL: UpdateUtils.UpdateChannel,
PLATFORM_VERSION: gApp.platformVersion,
DISTRIBUTION: getDistributionPrefValue(PREF_APP_DISTRIBUTION),
DISTRIBUTION_VERSION: getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION),
PING_COUNT: pingCountVersion,
TOTAL_PING_COUNT: pingCountTotal,
DAYS_SINCE_LAST_PING: daysSinceLastPing,
};
dsURI = dsURI.replace(/%([A-Z_]+)%/g, function(fullMatch, name) {
// Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
if (gAppVersion && (name == "APP_VERSION" || name == "VERSION")) {
return gAppVersion;
}
// Some items, like DAYS_SINCE_LAST_PING, can be undefined, so we can't just
// `return replacements[name] || fullMatch` or something like that.
if (!replacements.hasOwnProperty(name)) {
return fullMatch;
}
return replacements[name];
});
dsURI = dsURI.replace(/\+/g, "%2B");
// Under normal operations it will take around 5,883,516 years before the
@ -922,33 +922,38 @@ var Blocklist = {
versions: [],
prefs: [],
blockID: null,
attributes: new Map()
attributes: {},
// Atleast one of EXTENSION_BLOCK_FILTERS must get added to attributes
};
// Any filter starting with '/' is interpreted as a regex. So if an attribute
// starts with a '/' it must be checked via a regex.
function regExpCheck(attr) {
return attr.startsWith("/") ? parseRegExp(attr) : attr;
}
for (let filter of EXTENSION_BLOCK_FILTERS) {
let attr = blocklistElement.getAttribute(filter);
if (attr)
blockEntry.attributes.set(filter, regExpCheck(attr));
if (attr) {
// Any filter starting with '/' is interpreted as a regex. So if an attribute
// starts with a '/' it must be checked via a regex.
if (attr.startsWith("/")) {
let lastSlash = attr.lastIndexOf("/");
let pattern = attr.slice(1, lastSlash);
let flags = attr.slice(lastSlash + 1);
blockEntry.attributes[filter] = new RegExp(pattern, flags);
} else {
blockEntry.attributes[filter] = attr;
}
}
}
var children = blocklistElement.children;
for (let childElement of children) {
if (childElement.localName === "prefs") {
let localName = childElement.localName;
if (localName == "prefs" && childElement.hasChildNodes) {
let prefElements = childElement.children;
for (let prefElement of prefElements) {
if (prefElement.localName == "pref") {
blockEntry.prefs.push(prefElement.textContent);
}
}
} else if (childElement.localName === "versionRange") {
} else if (localName == "versionRange") {
blockEntry.versions.push(new BlocklistItemData(childElement));
}
}
@ -975,19 +980,23 @@ var Blocklist = {
};
var hasMatch = false;
for (let childElement of children) {
if (childElement.localName == "match") {
var name = childElement.getAttribute("name");
var exp = childElement.getAttribute("exp");
try {
blockEntry.matches[name] = new RegExp(exp, "m");
hasMatch = true;
} catch (e) {
// Ignore invalid regular expressions
}
} else if (childElement.localName == "versionRange") {
blockEntry.versions.push(new BlocklistItemData(childElement));
} else if (childElement.localName == "infoURL") {
blockEntry.infoURL = childElement.textContent;
switch (childElement.localName) {
case "match":
var name = childElement.getAttribute("name");
var exp = childElement.getAttribute("exp");
try {
blockEntry.matches[name] = new RegExp(exp, "m");
hasMatch = true;
} catch (e) {
// Ignore invalid regular expressions
}
break;
case "versionRange":
blockEntry.versions.push(new BlocklistItemData(childElement));
break;
case "infoURL":
blockEntry.infoURL = childElement.textContent;
break;
}
}
// Plugin entries require *something* to match to an actual plugin
@ -1095,11 +1104,11 @@ var Blocklist = {
return null;
// Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
if (!appVersion && !gApp.version)
if (!appVersion && !gAppVersion)
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
if (!appVersion)
appVersion = gApp.version;
appVersion = gAppVersion;
if (!toolkitVersion)
toolkitVersion = gApp.platformVersion;
@ -1409,35 +1418,35 @@ var Blocklist = {
* Helper for constructing a blocklist.
*/
function BlocklistItemData(versionRangeElement) {
var versionRange = this.getBlocklistVersionRange(versionRangeElement);
this.minVersion = versionRange.minVersion;
this.maxVersion = versionRange.maxVersion;
if (versionRangeElement && versionRangeElement.hasAttribute("severity"))
this.severity = versionRangeElement.getAttribute("severity");
else
this.severity = DEFAULT_SEVERITY;
if (versionRangeElement && versionRangeElement.hasAttribute("vulnerabilitystatus")) {
this.vulnerabilityStatus = versionRangeElement.getAttribute("vulnerabilitystatus");
} else {
this.vulnerabilityStatus = VULNERABILITYSTATUS_NONE;
}
this.targetApps = { };
var found = false;
this.targetApps = {};
let foundTarget = false;
this.severity = DEFAULT_SEVERITY;
this.vulnerabilityStatus = VULNERABILITYSTATUS_NONE;
if (versionRangeElement) {
for (let targetAppElement of versionRangeElement.children) {
if (targetAppElement.localName != "targetApplication")
continue;
found = true;
// default to the current application if id is not provided.
var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
let versionRange = this.getBlocklistVersionRange(versionRangeElement);
this.minVersion = versionRange.minVersion;
this.maxVersion = versionRange.maxVersion;
if (versionRangeElement.hasAttribute("severity"))
this.severity = versionRangeElement.getAttribute("severity");
if (versionRangeElement.hasAttribute("vulnerabilitystatus")) {
this.vulnerabilityStatus = versionRangeElement.getAttribute("vulnerabilitystatus");
}
for (let targetAppElement of versionRangeElement.children) {
if (targetAppElement.localName == "targetApplication") {
foundTarget = true;
// default to the current application if id is not provided.
let appID = targetAppElement.id || gAppID;
this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
}
}
} else {
this.minVersion = this.maxVersion = null;
}
// Default to all versions of the current application when no targetApplication
// elements were found
if (!found)
this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
if (!foundTarget)
this.targetApps[gAppID] = [{minVersion: null, maxVersion: null}];
}
BlocklistItemData.prototype = {
@ -1466,7 +1475,7 @@ BlocklistItemData.prototype = {
return false;
// Check if the application version matches
if (this.matchesTargetRange(gApp.ID, appVersion))
if (this.matchesTargetRange(gAppID, appVersion))
return true;
// Check if the toolkit version matches
@ -1541,7 +1550,7 @@ BlocklistItemData.prototype = {
// return minVersion = null and maxVersion = null if no specific versionRange
// elements were found
if (appVersions.length == 0)
appVersions.push(this.getBlocklistVersionRange(null));
appVersions.push({minVersion: null, maxVersion: null});
return appVersions;
},
@ -1558,15 +1567,9 @@ BlocklistItemData.prototype = {
* "maxVersion" The maximum version in a version range (default = null).
*/
getBlocklistVersionRange(versionRangeElement) {
var minVersion = null;
var maxVersion = null;
if (!versionRangeElement)
return { minVersion, maxVersion };
if (versionRangeElement.hasAttribute("minVersion"))
minVersion = versionRangeElement.getAttribute("minVersion");
if (versionRangeElement.hasAttribute("maxVersion"))
maxVersion = versionRangeElement.getAttribute("maxVersion");
// getAttribute returns null if the attribute is not present.
let minVersion = versionRangeElement.getAttribute("minVersion");
let maxVersion = versionRangeElement.getAttribute("maxVersion");
return { minVersion, maxVersion };
}

Просмотреть файл

@ -743,8 +743,13 @@ var AddonTestUtils = {
throw new Error("Attempting to startup manager that was already started.");
if (newVersion)
if (newVersion) {
this.appInfo.version = newVersion;
if (Cu.isModuleLoaded("resource://gre/modules/Blocklist.jsm")) {
let bsPassBlocklist = ChromeUtils.import("resource://gre/modules/Blocklist.jsm", {});
Object.defineProperty(bsPassBlocklist, "gAppVersion", {value: newVersion});
}
}
let XPIScope = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", null);
XPIScope.AsyncShutdown = MockAsyncShutdown;

Просмотреть файл

@ -791,6 +791,8 @@ add_task(async function update_schema_2() {
await changeXPIDBVersion(100);
gAppInfo.version = "2";
let bsPassBlocklist = ChromeUtils.import("resource://gre/modules/Blocklist.jsm", {});
Object.defineProperty(bsPassBlocklist, "gAppVersion", {value: "2"});
await promiseStartupManager();
let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
@ -815,6 +817,8 @@ add_task(async function update_schema_3() {
await promiseShutdownManager();
await changeXPIDBVersion(100);
gAppInfo.version = "2.5";
let bsPassBlocklist = ChromeUtils.import("resource://gre/modules/Blocklist.jsm", {});
Object.defineProperty(bsPassBlocklist, "gAppVersion", {value: "2.5"});
await promiseStartupManager();
let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
@ -848,6 +852,8 @@ add_task(async function update_schema_5() {
await changeXPIDBVersion(100);
gAppInfo.version = "1";
let bsPassBlocklist = ChromeUtils.import("resource://gre/modules/Blocklist.jsm", {});
Object.defineProperty(bsPassBlocklist, "gAppVersion", {value: "1"});
await promiseStartupManager();
let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);