Bug 475289 - Lazily initialize engineMetadataService. r=gavin
authorRyan Flint <rflint@ryanflint.com>
Fri, 02 Oct 2009 23:47:16 -0400
changeset 33411 fbf106437925b73a4261a22f5b7da9d96c2adaa1
parent 33410 6c4f06ec03003dfeb1d26243fffce67732146fc2
child 33412 0527053700f179b62f5cb804a55ee63375a60b93
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs475289
milestone1.9.3a1pre
Bug 475289 - Lazily initialize engineMetadataService. r=gavin
toolkit/components/search/nsSearchService.js
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -87,17 +87,17 @@ const SHERLOCK_FILE_EXT = "src";
 // Delay for lazy serialization (ms)
 const LAZY_SERIALIZE_DELAY = 100;
 
 // Delay for batching invalidation of the JSON cache (ms)
 const CACHE_INVALIDATION_DELAY = 1000;
 
 // Current cache version. This should be incremented if the format of the cache
 // file is modified.
-const CACHE_VERSION = 5;
+const CACHE_VERSION = 6;
 
 const ICON_DATAURL_PREFIX = "data:image/x-icon;base64,";
 
 // Supported extensions for Sherlock plugin icons
 const SHERLOCK_ICON_EXTENSIONS = [".gif", ".png", ".jpg", ".jpeg"];
 
 const NEW_LINES = /(\r\n|\r|\n)/;
 
@@ -1974,17 +1974,17 @@ Engine.prototype = {
   _initWithJSON: function SRCH_ENG__initWithJSON(aJson) {
     this.__id = aJson._id;
     this._name = aJson._name;
     this._description = aJson.description;
     if (aJson._hasPreferredIcon == undefined)
       this._hasPreferredIcon = true;
     else
       this._hasPreferredIcon = false;
-    this._hidden = aJson.hidden || null;
+    this._hidden = aJson._hidden;
     this._type = aJson.type || SEARCH_TYPE_MOZSEARCH;
     this._queryCharset = aJson.queryCharset || DEFAULT_QUERY_CHARSET;
     this.__searchForm = aJson.__searchForm;
     this.__installLocation = aJson._installLocation || SEARCH_APP_DIR;
     this._updateInterval = aJson._updateInterval || null;
     this._updateURL = aJson._updateURL || null;
     this._iconUpdateURL = aJson._iconUpdateURL || null;
     if (aJson._readOnly == undefined)
@@ -2007,16 +2007,17 @@ Engine.prototype = {
    *        Whether or not to filter out common default values. Recommended for
    *        use with _initWithJSON().
    * @returns An object suitable for serialization as JSON.
    **/
   _serializeToJSON: function SRCH_ENG__serializeToJSON(aFilter) {
     var json = {
       _id: this._id,
       _name: this._name,
+      _hidden: this.hidden,
       description: this.description,
       filePath: this._file.QueryInterface(Ci.nsILocalFile).persistentDescriptor,
       __searchForm: this.__searchForm,
       _iconURL: this._iconURL,
       _urls: [url._serializeToJSON() for each(url in this._urls)] 
     };
 
     if (this._installLocation != SEARCH_APP_DIR || !aFilter)
@@ -2024,18 +2025,16 @@ Engine.prototype = {
     if (this._updateInterval || !aFilter)
       json._updateInterval = this._updateInterval;
     if (this._updateURL || !aFilter)
       json._updateURL = this._updateURL;
     if (this._iconUpdateURL || !aFilter)
       json._iconUpdateURL = this._iconUpdateURL;
     if (!this._hasPreferredIcon || !aFilter)
       json._hasPreferredIcon = this._hasPreferredIcon;
-    if (this.hidden || !aFilter)
-      json.hidden = this.hidden;
     if (this.type != SEARCH_TYPE_MOZSEARCH || !aFilter)
       json.type = this.type;
     if (this.queryCharset != DEFAULT_QUERY_CHARSET || !aFilter)
       json.queryCharset = this.queryCharset;
     if (this._dataType != SEARCH_DATA_XML || !aFilter)
       json._dataType = this._dataType;
     if (!this._readOnly || !aFilter)
       json._readOnly = this._readOnly;
@@ -2173,17 +2172,17 @@ Engine.prototype = {
   },
 
   get description() {
     return this._description;
   },
 
   get hidden() {
     if (this._hidden === null)
-      this._hidden = engineMetadataService.getAttr(this, "hidden");
+      this._hidden = engineMetadataService.getAttr(this, "hidden") || false;
     return this._hidden;
   },
   set hidden(val) {
     var value = !!val;
     if (value != this._hidden) {
       this._hidden = value;
       engineMetadataService.setAttr(this, "hidden", value);
       notifyAction(this, SEARCH_ENGINE_CHANGED);
@@ -2392,35 +2391,37 @@ Submission.prototype = {
 }
 
 // nsIBrowserSearchService
 function SearchService() {
   this._init();
 }
 SearchService.prototype = {
   _engines: { },
-  _sortedEngines: null,
+  __sortedEngines: null,
+  get _sortedEngines() {
+    if (!this.__sortedEngines)
+      return this._buildSortedEngineList();
+    return this.__sortedEngines;
+  },
+
   // Whether or not we need to write the order of engines on shutdown. This
   // needs to happen anytime _sortedEngines is modified after initial startup. 
   _needToSetOrderPrefs: false,
 
   _init: function() {
     // Replace empty LOG function with the useful one if the log pref is set.
     if (getBoolPref(BROWSER_SEARCH_PREF + "log", false))
       LOG = DO_LOG;
 
-    engineMetadataService.init();
     engineUpdateService.init();
 
     this._loadEngines();
     this._addObservers();
 
-    // Now that all engines are loaded, build the sorted engine list
-    this._buildSortedEngineList();
-
     let selectedEngineName = getLocalizedPref(BROWSER_SEARCH_PREF +
                                               "selectedEngine");
     this._currentEngine = this.getEngineByName(selectedEngineName) ||
                           this.defaultEngine;
   },
 
   _buildCache: function SRCH_SVC__buildCache() {
     if (!getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true))
@@ -2600,21 +2601,21 @@ SearchService.prototype = {
 
       // Add the engine back
       this._engines[aEngine.name] = aEngine;
       notifyAction(aEngine, SEARCH_ENGINE_CHANGED);
     } else {
       // Not an update, just add the new engine.
       this._engines[aEngine.name] = aEngine;
       // Only add the engine to the list of sorted engines if the initial list
-      // has already been built (i.e. if this._sortedEngines is non-null). If
-      // it hasn't, we're still loading engines from disk, and will build the
-      // sorted engine list when that initial loading is done.
-      if (this._sortedEngines) {
-        this._sortedEngines.push(aEngine);
+      // has already been built (i.e. if this.__sortedEngines is non-null). If
+      // it hasn't, we're loading engines from disk and the sorted engine list
+      // will be built once we need it.
+      if (this.__sortedEngines) {
+        this.__sortedEngines.push(aEngine);
         this._needToSetOrderPrefs = true;
       }
       notifyAction(aEngine, SEARCH_ENGINE_ADDED);
     }
 
     if (aEngine._hasUpdates) {
       // Schedule the engine's next update, if it isn't already.
       if (!engineMetadataService.getAttr(aEngine, "updateexpir"))
@@ -2730,46 +2731,47 @@ SearchService.prototype = {
       names[i] = "order";
       values[i] = i + 1;
     }
 
     engineMetadataService.setAttrs(engines, names, values);
   },
 
   _buildSortedEngineList: function SRCH_SVC_buildSortedEngineList() {
+    LOG("_buildSortedEngineList: building list");
     var addedEngines = { };
-    this._sortedEngines = [];
+    this.__sortedEngines = [];
     var engine;
 
     // If the user has specified a custom engine order, read the order
     // information from the engineMetadataService instead of the default
     // prefs.
     if (getBoolPref(BROWSER_SEARCH_PREF + "useDBForOrder", false)) {
       for each (engine in this._engines) {
         var orderNumber = engineMetadataService.getAttr(engine, "order");
 
         // Since the DB isn't regularly cleared, and engine files may disappear
         // without us knowing, we may already have an engine in this slot. If
         // that happens, we just skip it - it will be added later on as an
         // unsorted engine. This problem will sort itself out when we call
         // _saveSortedEngineList at shutdown.
-        if (orderNumber && !this._sortedEngines[orderNumber-1]) {
-          this._sortedEngines[orderNumber-1] = engine;
+        if (orderNumber && !this.__sortedEngines[orderNumber-1]) {
+          this.__sortedEngines[orderNumber-1] = engine;
           addedEngines[engine.name] = engine;
         } else {
           // We need to call _saveSortedEngines so this gets sorted out.
           this._needToSetOrderPrefs = true;
         }
       }
 
       // Filter out any nulls for engines that may have been removed
-      var filteredEngines = this._sortedEngines.filter(function(a) { return !!a; });
-      if (this._sortedEngines.length != filteredEngines.length)
+      var filteredEngines = this.__sortedEngines.filter(function(a) { return !!a; });
+      if (this.__sortedEngines.length != filteredEngines.length)
         this._needToSetOrderPrefs = true;
-      this._sortedEngines = filteredEngines;
+      this.__sortedEngines = filteredEngines;
 
     } else {
       // The DB isn't being used, so just read the engine order from the prefs
       var i = 0;
       var engineName;
       var prefName;
 
       try {
@@ -2778,47 +2780,47 @@ SearchService.prototype = {
 
         for each (prefName in extras) {
           engineName = gPrefSvc.getCharPref(prefName);
 
           engine = this._engines[engineName];
           if (!engine || engine.name in addedEngines)
             continue;
 
-          this._sortedEngines.push(engine);
+          this.__sortedEngines.push(engine);
           addedEngines[engine.name] = engine;
         }
       }
       catch (e) { }
 
       while (true) {
         engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + (++i));
         if (!engineName)
           break;
 
         engine = this._engines[engineName];
         if (!engine || engine.name in addedEngines)
           continue;
         
-        this._sortedEngines.push(engine);
+        this.__sortedEngines.push(engine);
         addedEngines[engine.name] = engine;
       }
     }
 
     // Array for the remaining engines, alphabetically sorted
     var alphaEngines = [];
 
     for each (engine in this._engines) {
       if (!(engine.name in addedEngines))
         alphaEngines.push(this._engines[engine.name]);
     }
     alphaEngines = alphaEngines.sort(function (a, b) {
                                        return a.name.localeCompare(b.name);
                                      });
-    this._sortedEngines = this._sortedEngines.concat(alphaEngines);
+    return this.__sortedEngines = this.__sortedEngines.concat(alphaEngines);
   },
 
   /**
    * Converts a Sherlock file and its icon into the custom XML format used by
    * the Search Service. Saves the engine's icon (if present) into the XML as a
    * data: URI and changes the extension of the source file from ".src" to
    * ".xml". The engine data is then written to the file as XML.
    * @param aEngine
@@ -3089,17 +3091,17 @@ SearchService.prototype = {
       // Remove the engine file from disk (this might throw)
       engineToRemove._remove();
       engineToRemove._file = null;
 
       // Remove the engine from _sortedEngines
       var index = this._sortedEngines.indexOf(engineToRemove);
       if (index == -1)
         FAIL("Can't find engine to remove in _sortedEngines!", Cr.NS_ERROR_FAILURE);
-      this._sortedEngines.splice(index, 1);
+      this.__sortedEngines.splice(index, 1);
 
       // Remove the engine from the internal store
       delete this._engines[engineToRemove.name];
 
       notifyAction(engineToRemove, SEARCH_ENGINE_REMOVED);
 
       // Since we removed an engine, we need to update the preferences.
       this._needToSetOrderPrefs = true;
@@ -3141,18 +3143,18 @@ SearchService.prototype = {
       if (this._sortedEngines[i].hidden)
         aNewIndex++;
     }
 
     if (currentIndex == aNewIndex)
       return; // nothing to do!
 
     // Move the engine
-    var movedEngine = this._sortedEngines.splice(currentIndex, 1)[0];
-    this._sortedEngines.splice(aNewIndex, 0, movedEngine);
+    var movedEngine = this.__sortedEngines.splice(currentIndex, 1)[0];
+    this.__sortedEngines.splice(aNewIndex, 0, movedEngine);
 
     notifyAction(engine, SEARCH_ENGINE_CHANGED);
 
     // Since we moved an engine, we need to update the preferences.
     this._needToSetOrderPrefs = true;
   },
 
   restoreDefaultEngines: function SRCH_SVC_resetDefaultEngines() {
@@ -3250,51 +3252,65 @@ SearchService.prototype = {
         aIID.equals(Ci.nsIObserver)             ||
         aIID.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
 };
 
 var engineMetadataService = {
-  init: function epsInit() {
+  get mDB() {
     var engineDataTable = "id INTEGER PRIMARY KEY, engineid STRING, name STRING, value STRING";
     var file = getDir(NS_APP_USER_PROFILE_50_DIR);
     file.append("search.sqlite");
     var dbService = Cc["@mozilla.org/storage/service;1"].
                     getService(Ci.mozIStorageService);
+    var db;
     try {
-        this.mDB = dbService.openDatabase(file);
+      db = dbService.openDatabase(file);
     } catch (ex) {
-        if (ex.result == 0x8052000b) { /* NS_ERROR_FILE_CORRUPTED */
-            // delete and try again
-            file.remove(false);
-            this.mDB = dbService.openDatabase(file);
-        } else {
-            throw ex;
-        }
+      if (ex.result == 0x8052000b) { /* NS_ERROR_FILE_CORRUPTED */
+        // delete and try again
+        file.remove(false);
+        db = dbService.openDatabase(file);
+      } else {
+        throw ex;
+      }
     }
 
     try {
-      this.mDB.createTable("engine_data", engineDataTable);
+      db.createTable("engine_data", engineDataTable);
     } catch (ex) {
       // Fails if the table already exists, which is fine
     }
 
-    this.mGetData = this.mDB.createStatement (
+    delete this.mDB;
+    return this.mDB = db;
+  },
+
+  get mGetData() {
+    delete this.mGetData;
+    return this.mGetData = this.mDB.createStatement(
       "SELECT value FROM engine_data WHERE engineid = :engineid AND name = :name");
-    this.mDeleteData = this.mDB.createStatement (
+  },
+  get mDeleteData() {
+    delete this.mDeleteData;
+    return this.mDeleteData = this.mDB.createStatement(
       "DELETE FROM engine_data WHERE engineid = :engineid AND name = :name");
-    this.mInsertData = this.mDB.createStatement (
+  },
+  get mInsertData() {
+    delete this.mInsertData;
+    return this.mInsertData = this.mDB.createStatement(
       "INSERT INTO engine_data (engineid, name, value) " +
       "VALUES (:engineid, :name, :value)");
   },
+
   getAttr: function epsGetAttr(engine, name) {
-     // attr names must be lower case
-     name = name.toLowerCase();
+    // attr names must be lower case
+    name = name.toLowerCase();
 
     var stmt = this.mGetData;
     stmt.reset();
     var pp = stmt.params;
     pp.engineid = engine._id;
     pp.name = name;
 
     var value = null;