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

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

@ -743,8 +743,13 @@ var AddonTestUtils = {
throw new Error("Attempting to startup manager that was already started."); throw new Error("Attempting to startup manager that was already started.");
if (newVersion) if (newVersion) {
this.appInfo.version = 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); let XPIScope = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", null);
XPIScope.AsyncShutdown = MockAsyncShutdown; XPIScope.AsyncShutdown = MockAsyncShutdown;

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

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