Bug 1257565 - Refactor to (pre)load multiple files r=mossop
Refactor to (pre)load multiple filesns/nsBlocklistService.js
MozReview-Commit-ID: 15DsdekCdze
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -273,16 +273,18 @@ function parseRegExp(aStr) {
/**
* Manages the Blocklist. The Blocklist is a representation of the contents of
* blocklist.xml and allows us to remotely disable / re-enable blocklisted
* items managed by the Extension Manager with an item's appDisabled property.
* It also blocklists plugins with data from blocklist.xml.
*/
function Blocklist() {
+ this._preloadedBlocklistContent = new Map();
+
Services.obs.addObserver(this, "xpcom-shutdown");
Services.obs.addObserver(this, "sessionstore-windows-restored");
gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
MAX_BLOCK_LEVEL);
gPref.addObserver("extensions.blocklist.", this);
gPref.addObserver(PREF_EM_LOGGING_ENABLED, this);
@@ -658,21 +660,18 @@ Blocklist.prototype = {
if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
(request.status != 200 && request.status != 0)) {
LOG("Blocklist::onXMLLoad: there was an error during load");
return;
}
var oldAddonEntries = this._addonEntries;
var oldPluginEntries = this._pluginEntries;
- this._addonEntries = [];
- this._gfxEntries = [];
- this._pluginEntries = [];
- this._loadBlocklistFromString(request.responseText);
+ this._loadBlocklistFromXMLString(request.responseText);
// We don't inform the users when the graphics blocklist changed at runtime.
// However addons and plugins blocking status is refreshed.
this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
try {
let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
await OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
} catch (e) {
@@ -701,30 +700,134 @@ Blocklist.prototype = {
statusText);
},
/**
* Finds the newest blocklist file from the application and the profile and
* load it or does nothing if neither exist.
*/
_loadBlocklist() {
- this._addonEntries = [];
- this._gfxEntries = [];
- this._pluginEntries = [];
- var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
- if (profFile.exists()) {
- this._loadBlocklistFromFile(profFile);
+ if (!gBlocklistEnabled) {
+ LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
+ return;
+ }
+ const content = this._loadBlocklistFromFile(FILE_BLOCKLIST);
+ this._loadBlocklistFromXMLString(content);
+ },
+
+ _loadBlocklistFromFile(filename) {
+ let file = FileUtils.getFile(KEY_PROFILEDIR, [filename]);
+ if (!file.exists()) {
+ let appFile = FileUtils.getFile(KEY_APPDIR, [filename]);
+ if (appFile.exists()) {
+ file = appFile;
+ }
+ }
+
+ let telemetry = Services.telemetry;
+
+ // Check if preloaded content exists for this file.
+ if (this._preloadedBlocklistContent.has(file.path)) {
+ telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(false);
+ const text = this._preloadedBlocklistContent.get(file.path);
+ this._preloadedBlocklistContent.delete(file.path);
+ return text;
+ }
+
+ if (!file.exists()) {
+ LOG("Blocklist::_loadBlocklistFromFile: File does not exist " + file.path);
return;
}
- var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
- if (appFile.exists()) {
- this._loadBlocklistFromFile(appFile);
+
+ telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(true);
+
+ let text = "";
+ let fstream = null;
+ let cstream = null;
+
+ try {
+ fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
+ .createInstance(Components.interfaces.nsIConverterInputStream);
+
+ fstream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ let str = {};
+ let read = 0;
+
+ do {
+ read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
+ text += str.value;
+ } while (read != 0);
+ } catch (e) {
+ LOG("Blocklist::_loadBlocklistFromFile: Failed to load file " + e);
+ } finally {
+ if (cstream)
+ cstream.close();
+ if (fstream)
+ fstream.close();
+ }
+
+ return text;
+ },
+
+ _isBlocklistLoaded() {
+ return this._addonEntries != null && this._gfxEntries != null && this._pluginEntries != null;
+ },
+
+ /* Used for testing */
+ _clear() {
+ this._addonEntries = null;
+ this._gfxEntries = null;
+ this._pluginEntries = null;
+ this._preloadedBlocklistContent.clear();
+ },
+
+ async _preloadBlocklist() {
+ if (!gBlocklistEnabled) {
+ LOG("Blocklist::_preloadBlocklist: blocklist is disabled");
return;
}
- LOG("Blocklist::_loadBlocklist: no XML File found");
+
+ await this._preloadBlocklistFile(FILE_BLOCKLIST);
+ }),
+
+ async _preloadBlocklistFile(filename) {
+ let file = FileUtils.getFile(KEY_PROFILEDIR, [filename]);
+ if (!file.exists()) {
+ let appFile = FileUtils.getFile(KEY_APPDIR, [filename]);
+ if (appFile.exists()) {
+ file = appFile;
+ } else {
+ LOG(`Blocklist::_preloadBlocklistFile: no ${filename} file found`);
+ return;
+ }
+ }
+ const path = file.path;
+ if (this._preloadedBlocklistContent.has(path)) {
+ // The file has been already loaded.
+ return;
+ }
+
+ try {
+ let content = await OS.File.read(path, { encoding: "utf-8" });
+
+ if (!this._isBlocklistLoaded()) {
+ // Store the content only if a sync load has not been performed in the meantime.
+ this._preloadedBlocklistContent.set(path, content);
+ }
+ } catch (e) {
+ LOG(`Blocklist::_preloadBlocklistFile: Failed to load ${path} file : ${e}`);
+ }
+ },
+
+ _isBlocklistLoaded() {
+ return this._addonEntries != null && this._gfxEntries != null && this._pluginEntries != null;
},
/**
# The blocklist XML file looks something like this:
#
# <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
# <emItems>
# <emItem id="item_1@domain" blockID="i1">
@@ -782,134 +885,27 @@ Blocklist.prototype = {
# <serialNumber>AkHVNA==</serialNumber>
# </certItem>
# <!-- subject is the DER subject name data base64 encoded... -->
# <certItem subject="MA0xCzAJBgNVBAMMAmNh" pubKeyHash="/xeHA5s+i9/z9d8qy6JEuE1xGoRYIwgJuTE/lmaGJ7M=">
# </certItem>
# </certItems>
# </blocklist>
*/
-
- _loadBlocklistFromFile(file) {
- if (!gBlocklistEnabled) {
- LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
- return;
- }
-
- let telemetry = Services.telemetry;
-
- if (this._isBlocklistPreloaded()) {
- telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(false);
- this._loadBlocklistFromString(this._preloadedBlocklistContent);
- delete this._preloadedBlocklistContent;
- return;
- }
-
- if (!file.exists()) {
- LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist " + file.path);
- return;
- }
-
- telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(true);
-
- let text = "";
- let fstream = null;
- let cstream = null;
-
- try {
- fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
- .createInstance(Components.interfaces.nsIFileInputStream);
- cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
- .createInstance(Components.interfaces.nsIConverterInputStream);
-
- fstream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
- cstream.init(fstream, "UTF-8", 0, 0);
-
- let str = {};
- let read = 0;
-
- do {
- read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
- text += str.value;
- } while (read != 0);
- } catch (e) {
- LOG("Blocklist::_loadBlocklistFromFile: Failed to load XML file " + e);
- } finally {
- if (cstream)
- cstream.close();
- if (fstream)
- fstream.close();
- }
-
- if (text)
- this._loadBlocklistFromString(text);
- },
+ _loadBlocklistFromXMLString(text) {
+ this._addonEntries = [];
+ this._gfxEntries = [];
+ this._pluginEntries = [];
- _isBlocklistLoaded() {
- return this._addonEntries != null && this._gfxEntries != null && this._pluginEntries != null;
- },
-
- _isBlocklistPreloaded() {
- return this._preloadedBlocklistContent != null;
- },
-
- /* Used for testing */
- _clear() {
- this._addonEntries = null;
- this._gfxEntries = null;
- this._pluginEntries = null;
- this._preloadedBlocklistContent = null;
- },
-
- async _preloadBlocklist() {
- let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
- try {
- await this._preloadBlocklistFile(profPath);
- return;
- } catch (e) {
- LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e)
- }
-
- var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
- try {
- await this._preloadBlocklistFile(appFile.path);
- return;
- } catch (e) {
- LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e)
- }
-
- LOG("Blocklist::_preloadBlocklist: no XML File found");
- },
-
- async _preloadBlocklistFile(path) {
- if (this._addonEntries) {
- // The file has been already loaded.
- return;
- }
-
- if (!gBlocklistEnabled) {
- LOG("Blocklist::_preloadBlocklistFile: blocklist is disabled");
- return;
- }
-
- let text = await OS.File.read(path, { encoding: "utf-8" });
-
- if (!this._addonEntries) {
- // Store the content only if a sync load has not been performed in the meantime.
- this._preloadedBlocklistContent = text;
- }
- },
-
- _loadBlocklistFromString(text) {
try {
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = parser.parseFromString(text, "text/xml");
if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
- LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
+ LOG("Blocklist::_loadBlocklistFromXMLString: aborting due to incorrect " +
"XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
"Received: " + doc.documentElement.namespaceURI);
return;
}
var populateCertBlocklist = getPref("getBoolPref", PREF_ONECRL_VIA_AMO, true);
var childNodes = doc.documentElement.childNodes;
@@ -932,27 +928,27 @@ Blocklist.prototype = {
}
break;
case "gfxItems":
// Parse as simple list of objects.
this._gfxEntries = this._processItemNodes(element.childNodes, "gfxBlacklistEntry",
this._handleGfxBlacklistNode);
break;
default:
- LOG("Blocklist::_loadBlocklistFromString: ignored entries " + element.localName);
+ LOG("Blocklist::_loadBlocklistFromXMLString: ignored entries " + element.localName);
}
}
if (populateCertBlocklist) {
gCertBlocklistService.saveEntries();
}
if (this._gfxEntries.length > 0) {
this._notifyObserversBlocklistGFX();
}
} catch (e) {
- LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
+ LOG("Blocklist::_loadBlocklistFromXMLString: Error constructing blocklist " + e);
}
},
_processItemNodes(itemNodes, itemName, handler) {
var result = [];
for (var i = 0; i < itemNodes.length; ++i) {
var blocklistElement = itemNodes.item(i);
if (!(blocklistElement instanceof Ci.nsIDOMElement) ||
--- a/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
@@ -1,44 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+
+const KEY_PROFILEDIR = "ProfD";
+
function run_test() {
run_next_test();
}
add_task(async function() {
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService().wrappedJSObject;
let scope = Components.utils.import("resource://gre/modules/osfile.jsm", {});
+ const blocklistPath = FileUtils.getFile(KEY_PROFILEDIR, ["blocklist.xml"]).path;
+
// sync -> async
blocklist._loadBlocklist();
do_check_true(blocklist._isBlocklistLoaded());
await blocklist._preloadBlocklist();
- do_check_false(blocklist._isBlocklistPreloaded());
+ do_check_false(blocklist._preloadedBlocklistContent.has(blocklistPath));
blocklist._clear();
// async -> sync
await blocklist._preloadBlocklist();
do_check_false(blocklist._isBlocklistLoaded());
- do_check_true(blocklist._isBlocklistPreloaded());
+ do_check_true(blocklist._preloadedBlocklistContent.has(blocklistPath));
blocklist._loadBlocklist();
do_check_true(blocklist._isBlocklistLoaded());
- do_check_false(blocklist._isBlocklistPreloaded());
+ do_check_false(blocklist._preloadedBlocklistContent.has(blocklistPath));
blocklist._clear();
// async -> sync -> async
let read = scope.OS.File.read;
scope.OS.File.read = function(...args) {
return new Promise((resolve, reject) => {
do_execute_soon(() => {
blocklist._loadBlocklist();
resolve(read(...args));
});
});
}
await blocklist._preloadBlocklist();
do_check_true(blocklist._isBlocklistLoaded());
- do_check_false(blocklist._isBlocklistPreloaded());
+ do_check_false(blocklist._preloadedBlocklistContent.has(blocklistPath));
});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_gfx.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_gfx.js
@@ -58,17 +58,17 @@ add_task(async function test_parsing_fai
" <devices>" +
" <device>0x2,582</device>" +
" <device>0x2782</device>" +
" </devices>" +
" </gfxBlacklistEntry>" +
"</gfxItems>" +
"</blocklist>";
const blocklist = Blocklist();
- blocklist._loadBlocklistFromString(input);
+ blocklist._loadBlocklistFromXMLString(input);
equal(blocklist._gfxEntries[0].devices.length, 1);
equal(blocklist._gfxEntries[0].devices[0], "0x2782");
});
add_task(async function test_empty_values_are_ignored() {
const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
"<gfxItems>" +
@@ -76,34 +76,34 @@ add_task(async function test_empty_value
" <os></os>" +
" </gfxBlacklistEntry>" +
"</gfxItems>" +
"</blocklist>";
const blocklist = Blocklist();
let received;
const observe = (subject, topic, data) => { received = data };
Services.obs.addObserver(observe, EVENT_NAME);
- blocklist._loadBlocklistFromString(input);
+ blocklist._loadBlocklistFromXMLString(input);
ok(received.indexOf("os" < 0));
Services.obs.removeObserver(observe, EVENT_NAME);
});
add_task(async function test_empty_devices_are_ignored() {
const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
"<gfxItems>" +
" <gfxBlacklistEntry>" +
" <devices></devices>" +
" </gfxBlacklistEntry>" +
"</gfxItems>" +
"</blocklist>";
const blocklist = Blocklist();
let received;
const observe = (subject, topic, data) => { received = data };
Services.obs.addObserver(observe, EVENT_NAME);
- blocklist._loadBlocklistFromString(input);
+ blocklist._loadBlocklistFromXMLString(input);
ok(received.indexOf("devices" < 0));
Services.obs.removeObserver(observe, EVENT_NAME);
});
add_task(async function test_version_range_default_values() {
const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
"<gfxItems>" +
" <gfxBlacklistEntry>" +
@@ -119,17 +119,17 @@ add_task(async function test_version_ran
" <versionRange minVersion=\" \"/>" +
" </gfxBlacklistEntry>" +
" <gfxBlacklistEntry>" +
" <versionRange/>" +
" </gfxBlacklistEntry>" +
"</gfxItems>" +
"</blocklist>";
const blocklist = Blocklist();
- blocklist._loadBlocklistFromString(input);
+ blocklist._loadBlocklistFromXMLString(input);
equal(blocklist._gfxEntries[0].versionRange.minVersion, "13.0b2");
equal(blocklist._gfxEntries[0].versionRange.maxVersion, "42.0");
equal(blocklist._gfxEntries[1].versionRange.minVersion, "0");
equal(blocklist._gfxEntries[1].versionRange.maxVersion, "2.0");
equal(blocklist._gfxEntries[2].versionRange.minVersion, "1.0");
equal(blocklist._gfxEntries[2].versionRange.maxVersion, "*");
equal(blocklist._gfxEntries[3].versionRange.minVersion, "0");
equal(blocklist._gfxEntries[3].versionRange.maxVersion, "*");
@@ -144,12 +144,12 @@ add_task(async function test_blockid_att
" <vendor> 0x10de </vendor>" +
" </gfxBlacklistEntry>" +
" <gfxBlacklistEntry>" +
" <feature> DIRECT3D_9_LAYERS </feature>" +
" </gfxBlacklistEntry>" +
"</gfxItems>" +
"</blocklist>";
const blocklist = Blocklist();
- blocklist._loadBlocklistFromString(input);
+ blocklist._loadBlocklistFromXMLString(input);
equal(blocklist._gfxEntries[0].blockID, "g60");
ok(!blocklist._gfxEntries[1].hasOwnProperty("blockID"));
});