Bug 989419 - Remove main-thread IO from background update of {profile}\blocklist.xml. r=irving
authorRoberto A. Vitillo <rvitillo@mozilla.com>
Wed, 16 Apr 2014 13:50:37 -0400
changeset 179332 5cf9041418fa3033411ef2d3fcc84efe030c1c32
parent 179331 26a034a63189bc98e4452311434fe56b2a6b8016
child 179333 be14df51e362a840d94fc7826edcfc4639dc300d
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersirving
bugs989419
milestone31.0a1
Bug 989419 - Remove main-thread IO from background update of {profile}\blocklist.xml. r=irving
toolkit/mozapps/extensions/nsBlocklistService.js
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -13,16 +13,20 @@ const Cr = Components.results;
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
                                   "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+                                  "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org"
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
 const FILE_BLOCKLIST                  = "blocklist.xml";
 const PREF_BLOCKLIST_LASTUPDATETIME   = "app.update.lastUpdateTime.blocklist-background-update-timer";
 const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
 const PREF_BLOCKLIST_ITEM_URL         = "extensions.blocklist.itemURL";
@@ -562,47 +566,47 @@ Blocklist.prototype = {
     request.send(null);
 
     // When the blocklist loads we need to compare it to the current copy so
     // make sure we have loaded it.
     if (!this._addonEntries)
       this._loadBlocklist();
   },
 
-  onXMLLoad: function Blocklist_onXMLLoad(aEvent) {
-    var request = aEvent.target;
+  onXMLLoad: Task.async(function* (aEvent) {
+    let request = aEvent.target;
     try {
       gCertUtils.checkCert(request.channel);
     }
     catch (e) {
       LOG("Blocklist::onXMLLoad: " + e);
       return;
     }
-    var responseXML = request.responseXML;
+    let responseXML = request.responseXML;
     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 blocklistFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
-    if (blocklistFile.exists())
-      blocklistFile.remove(false);
-    var fos = FileUtils.openSafeFileOutputStream(blocklistFile);
-    fos.write(request.responseText, request.responseText.length);
-    FileUtils.closeSafeFileOutputStream(fos);
 
     var oldAddonEntries = this._addonEntries;
     var oldPluginEntries = this._pluginEntries;
     this._addonEntries = [];
     this._pluginEntries = [];
-    this._loadBlocklistFromFile(FileUtils.getFile(KEY_PROFILEDIR,
-                                                  [FILE_BLOCKLIST]));
+
+    this._loadBlocklistFromString(request.responseText);
+    this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
 
-    this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
-  },
+    try {
+      let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
+      yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
+    } catch (e) {
+      LOG("Blocklist::onXMLLoad: " + e);
+    }
+  }),
 
   onXMLError: function Blocklist_onXMLError(aEvent) {
     try {
       var request = aEvent.target;
       // the following may throw (e.g. a local file or timeout)
       var status = request.status;
     }
     catch (e) {
@@ -699,27 +703,56 @@ Blocklist.prototype = {
 
   _loadBlocklistFromFile: function Blocklist_loadBlocklistFromFile(file) {
     if (!gBlocklistEnabled) {
       LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
       return;
     }
 
     if (!file.exists()) {
-      LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
+      LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist " + file.path);
       return;
     }
 
-    var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
-                               .createInstance(Components.interfaces.nsIFileInputStream);
-    fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+    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 {
+      cstream.close();
+      fstream.close();
+    }
+
+    text && this._loadBlocklistFromString(text);
+  },
+
+  _loadBlocklistFromString : function Blocklist_loadBlocklistFromString(text) {
     try {
       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
                    createInstance(Ci.nsIDOMParser);
-      var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
+      var doc = parser.parseFromString(text, "text/xml");
       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
             "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
             "Received: " + doc.documentElement.namespaceURI);
         return;
       }
 
       var childNodes = doc.documentElement.childNodes;
@@ -741,17 +774,16 @@ Blocklist.prototype = {
                                        null);
         }
       }
     }
     catch (e) {
       LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
       return;
     }
-    fileStream.close();
   },
 
   _processItemNodes: function Blocklist_processItemNodes(itemNodes, prefix, handler) {
     var result = [];
     var itemName = prefix + "Item";
     for (var i = 0; i < itemNodes.length; ++i) {
       var blocklistElement = itemNodes.item(i);
       if (!(blocklistElement instanceof Ci.nsIDOMElement) ||