342952
Produced: 2006 07 20 4:25:31 PM
   
Mode:  Differences with Context  
   
Left file: I:\search-update-3.diff     Right file: I:\search-update-4.diff  
Index: browser/components/search/nsIBrowserSearchService.idl = Index: browser/components/search/nsIBrowserSearchService.idl
===================================================================   ===================================================================
RCS file: /cvsroot/mozilla/browser/components/search/nsIBrowserSearchService.idl,v   RCS file: /cvsroot/mozilla/browser/components/search/nsIBrowserSearchService.idl,v
retrieving revision 1.1.2.11   retrieving revision 1.1.2.13
diff -u -p -8 -r1.1.2.11 nsIBrowserSearchService.idl   diff -u -p -8 -r1.1.2.13 nsIBrowserSearchService.idl
--- browser/components/search/nsIBrowserSearchService.idl 23 Jun 2006 01:06:59 -0000  1.1.2.11   --- browser/components/search/nsIBrowserSearchService.idl 18 Jul 2006 19:00:24 -0000  1.1.2.13
+++ browser/components/search/nsIBrowserSearchService.idl 15 Jul 2006 04:54:37 -0000   +++ browser/components/search/nsIBrowserSearchService.idl 20 Jul 2006 20:03:44 -0000
@@ -167,33 +167,33 @@ interface nsIBrowserSearchService : nsIS   @@ -168,17 +168,17 @@ interface nsIBrowserSearchService : nsIS
{ +-  
   /** =    /**
    * Adds a new search engine from the file at the supplied URI and optionally <>     * Adds a new search engine from the file at the supplied URI, optionally
    * selects it as the current engine.       * asking the user for confirmation first. The confirmation dialog also
        * offers the option to begin using the newly added engine right away.
    * =     *
    * @param engineURL       * @param engineURL
    *        The URL to the search engine's description file.       *        The URL to the search engine's description file.
    *       *
-   * @param type   -   * @param type
+   * @param dataType   +   * @param dataType
    *        An integer representing the plugin file format. Must be one       *        An integer representing the plugin file format. Must be one
    *        of the supported search engine data types defined above.       *        of the supported search engine data types defined above.
    *       *
    * @param iconURL       * @param iconURL
    *        A URL string to an icon file to be used as the search engine's       *        A URL string to an icon file to be used as the search engine's
    *        icon. This value may be overridden by an icon specified in the       *        icon. This value may be overridden by an icon specified in the
    *        engine description file.       *        engine description file.
    *       *
    * @param select <> @@ -186,17 +186,17 @@ interface nsIBrowserSearchService : nsIS
    *        A boolean value indicating whether the newly added engine should       *        A boolean value indicating whether the user should be asked for
        *        confirmation before this engine is added to the list.  If this
        *        value is false, the engine will be added to the list upon successful
    *        be selected as the current engine after it finishes loading.       *        load, but it will not be selected as the current engine.
    * =     *
    * @throws NS_ERROR_FAILURE if the type is invalid, or if the description       * @throws NS_ERROR_FAILURE if the type is invalid, or if the description
    *         file cannot be successfully loaded.       *         file cannot be successfully loaded.
    */       */
-  void addEngine(in AString engineURL, in long type, in AString iconURL,   -  void addEngine(in AString engineURL, in long type, in AString iconURL,
+  void addEngine(in AString engineURL, in long dataType, in AString iconURL,   +  void addEngine(in AString engineURL, in long dataType, in AString iconURL,
                  in boolean select); <>                   in boolean confirm);
=
   /**      /**
    * Adds a new search engine. <>     * Adds a new search engine, without asking the user for confirmation.
    * =     *
    * @param name       * @param name
    *        The search engine's name. Must be unique. Must not be null.       *        The search engine's name. Must be unique. Must not be null.
    *       *
Index: browser/components/search/nsSearchService.js   Index: browser/components/search/nsSearchService.js
===================================================================   ===================================================================
RCS file: /cvsroot/mozilla/browser/components/search/nsSearchService.js,v   RCS file: /cvsroot/mozilla/browser/components/search/nsSearchService.js,v
retrieving revision 1.1.2.43   retrieving revision 1.1.2.45
diff -u -p -8 -r1.1.2.43 nsSearchService.js   diff -u -p -8 -r1.1.2.45 nsSearchService.js
--- browser/components/search/nsSearchService.js  6 Jul 2006 02:57:27 -0000 1.1.2.43   --- browser/components/search/nsSearchService.js  18 Jul 2006 19:00:24 -0000  1.1.2.45
+++ browser/components/search/nsSearchService.js  15 Jul 2006 04:54:39 -0000   +++ browser/components/search/nsSearchService.js  20 Jul 2006 20:03:45 -0000
@@ -170,16 +170,20 @@ const OS_PARAM_OPTIONAL     = /\{\w+\?\}   @@ -171,16 +171,20 @@ const OS_PARAM_OPTIONAL     = /\{\w+\?\}
// required, since our values are just really arbitrary "guesses" that should   // required, since our values are just really arbitrary "guesses" that should
// give us the output we want.   // give us the output we want.
 
   // The engine's description =    // The engine's description
   _description: "",      _description: "",
+  // Used to store the engine to replace, if we're an update to an existing   +  // Used to store the engine to replace, if we're an update to an existing
+  // engine.   +  // engine.
+  _engineToUpdate: null,   +  _engineToUpdate: null,
   // The file from which the plugin was loaded.      // The file from which the plugin was loaded.
   _file: null,      _file: null,
   // Set to true if the engine has a preferred icon (an icon that should not be      // Set to true if the engine has a preferred icon (an icon that should not be
   // overridden by a non-preferred icon).      // overridden by a non-preferred icon).
   _hasPreferredIcon: null,      _hasPreferredIcon: null,
   // Whether the engine is hidden from the user.      // Whether the engine is hidden from the user.
   _hidden: null,      _hidden: null,
   // The engine's name.      // The engine's name.
@@ -961,28 +969,34 @@ Engine.prototype = {   @@ -967,28 +975,34 @@ Engine.prototype = {
   // A URL string pointing to the engine's search form. <>    // Whether to obtain user confirmation before adding the engine. This is only
       // used when the engine is first added to the list.
   _searchForm: null,      _confirm: false,
   // The URI object from which the engine was retrieved.      // Whether to set this as the current engine as soon as it is loaded.  This
   // This is null for local plugins, and is used for error messages, logging,    
   // and determining whether to start using a newly added engine right away.      // is only used when the engine is first added to the list.
   _uri: null,      _useNow: false,
   // Whether the search engine file is in the app dir. =    // Whether the search engine file is in the app dir.
   __isInAppDir: null,      __isInAppDir: null,
+  // The number of days between update checks for new versions   +  // The number of days between update checks for new versions
+  _updateInterval: null,   +  _updateInterval: null,
+  // The url to check at for a new update   +  // The url to check at for a new update
+  _updateURL: null,   +  _updateURL: null,
+  // The url to check for a new icon   +  // The url to check for a new icon
+  _iconUpdateURL: null,   +  _iconUpdateURL: null,
 
   /**      /**
    * Retrieves the data from the engine's file. If the engine's dataType is       * Retrieves the data from the engine's file. If the engine's dataType is
    * XML, the document element is placed in the engine's data field. For text       * XML, the document element is placed in the engine's data field. For text
    * engines, the data is just read directly from file and placed as an array       * engines, the data is just read directly from file and placed as an array
    * of lines in the engine's data field.       * of lines in the engine's data field.
 
     if (!aBytes) { =      if (!aBytes) {
       onError();          onError();
       return;          return;
     }        }
 
+    var engineToUpdate = null;   +    var engineToUpdate = null;
+    if (aEngine._engineToUpdate) {   +    if (aEngine._engineToUpdate) {
+      engineToUpdate = aEngine._engineToUpdate.wrappedJSObject;   +      engineToUpdate = aEngine._engineToUpdate.wrappedJSObject;
+   +
+      if (engineToUpdate._readOnly) {   +      if (engineToUpdate._readOnly) {
+        LOG("_onLoad: Can't update readonly engine!");   +        LOG("_onLoad: Can't update readonly engine!");
+        return;   +        return;
+      }   +      }
+   +
+      // Make this new engine use the old engine's file. We need to do this now <> +      // Make this new engine use the old engine's file.
+      // because otherwise the _initFromData() call below will give the engine    
+      // a unique file.    
+      aEngine._file = engineToUpdate._file; = +      aEngine._file = engineToUpdate._file;
+    }   +    }
+   +
     switch (aEngine._dataType) {        switch (aEngine._dataType) {
       case SEARCH_DATA_XML:          case SEARCH_DATA_XML:
         var dataString = bytesToString(aBytes, "UTF-8");            var dataString = bytesToString(aBytes, "UTF-8");
         ENSURE(dataString, "_onLoad: Couldn't convert byte array!",            ENSURE(dataString, "_onLoad: Couldn't convert byte array!",
                Cr.NS_ERROR_FAILURE);                   Cr.NS_ERROR_FAILURE);
         var parser = Cc["@mozilla.org/xmlextras/domparser;1"].            var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
                      createInstance(Ci.nsIDOMParser);                         createInstance(Ci.nsIDOMParser);
         var doc = parser.parseFromString(dataString, "text/xml");            var doc = parser.parseFromString(dataString, "text/xml");
@@ -1096,30 +1134,43 @@ Engine.prototype = {   @@ -1137,16 +1173,17 @@ Engine.prototype = {
       case SEARCH_DATA_TEXT:          case SEARCH_DATA_TEXT:
         aEngine._data = aBytes;            aEngine._data = aBytes;
 
         onError(); =          onError();
         LOG("_onLoad: Bogus engine _dataType: \"" + this._dataType + "\"");            LOG("_onLoad: Bogus engine _dataType: \"" + this._dataType + "\"");
         return;            return;
     }        }
+   +
     try {        try {
       // Initialize the engine from the obtained data          // Initialize the engine from the obtained data
       aEngine._initFromData();          aEngine._initFromData();
     } catch (ex) {        } catch (ex) {
       LOG("_onLoad: Failed to init engine!\n" + ex);          LOG("_onLoad: Failed to init engine!\n" + ex);
       // Report an error to the user          // Report an error to the user
       onError();          onError();
       return;          return;
    @@ -1157,23 +1194,37 @@ Engine.prototype = {
  -+        var confirmation = aEngine._confirmAddEngine();
           LOG("_onLoad: confirm is " + confirmation.confirmed +
               "; useNow is " + confirmation.useNow);
           if (!confirmation.confirmed)
             return;
           aEngine._useNow = confirmation.useNow;
     } =      }
 
  -+ -    // Since we're coming from a URL, we don't yet have a file.
    -    aEngine._file = getSanitizedFile(aEngine.name);
    +    // If we don't yet have a file, get one now. The only case where we would
    +    // already having a file is if this is an update and _file was set above.
    +    if (!aEngine._file)
    +      aEngine._file = getSanitizedFile(aEngine.name);
    +
+    if (engineToUpdate) { = +    if (engineToUpdate) {
+      // Keep track of the last modified date, so that we can make conditional   +      // Keep track of the last modified date, so that we can make conditional
+      // requests for future updates.   +      // requests for future updates.
+      engineMetadataService.setAttr(aEngine, "updatelastmodified",   +      engineMetadataService.setAttr(aEngine, "updatelastmodified",
+                                    (new Date()).toUTCString());   +                                    (new Date()).toUTCString());
+   +
+      // Set the new engine's icon, if it doesn't yet have one.   +      // Set the new engine's icon, if it doesn't yet have one.
+      if (!aEngine._iconURI && engineToUpdate._iconURI)   +      if (!aEngine._iconURI && engineToUpdate._iconURI)
+        aEngine._iconURI = engineToUpdate._iconURI;   +        aEngine._iconURI = engineToUpdate._iconURI;
+    }   +    }
+ <>
     // Write the engine to file =      // Write the engine to file
     aEngine._serializeToFile();        aEngine._serializeToFile();
 
-    // Notify the search service of the sucessful load   -    // Notify the search service of the sucessful load
+    // Notify the search service of the sucessful load. It will deal with   +    // Notify the search service of the sucessful load. It will deal with
+    // updates by checking aEngine._engineToUpdate.   +    // updates by checking aEngine._engineToUpdate.
     notifyAction(aEngine, SEARCH_ENGINE_LOADED);        notifyAction(aEngine, SEARCH_ENGINE_LOADED);
   },      },
 
   /**      /**
    * Sets the .iconURI property of the engine.       * Sets the .iconURI property of the engine.
    *       *
    *  @param aIconURL       *  @param aIconURL
    *         A URI string pointing to the engine's icon. Must have a http[s],       *         A URI string pointing to the engine's icon. Must have a http[s],
 
     // Serialize the engine first - we don't want to overwrite a good file =      // Serialize the engine first - we don't want to overwrite a good file
     // if this somehow fails.        // if this somehow fails.
     doc = this._serializeToElement();        doc = this._serializeToElement();
@@ -1987,28 +2054,30 @@ Submission.prototype = {   @@ -2030,20 +2097,22 @@ Submission.prototype = {
}   }
 
// nsIBrowserSearchService   // nsIBrowserSearchService
function SearchService() {   function SearchService() {
   this._init();      this._init();
}   }
SearchService.prototype = {   SearchService.prototype = {
   _engines: { },      _engines: { },
-  _sortedEngines: [],   -  _sortedEngines: [],
+  _sortedEngines: null,   +  _sortedEngines: null,
+-  
   // If this is set to the URI of the description of a search engine being added    
   // to the list (typically by calling addEngine()), that engine will be    
   // selected as the current engine when it finishes loading.  If another    
   // engine is added with "start using this one now" before the first selected    
   // engine finishes loading, the second choice will override the first one.    
   // If the selected engine fails to load, this marker will be cleared.    
   _selectNewEngineURI: null,    
=
   _init: function() {      _init: function() {
     engineMetadataService.init();        engineMetadataService.init();
+    engineUpdateService.init();   +    engineUpdateService.init();
+   +
     this._addObservers();        this._addObservers();
 
     var fileLocator = Cc["@mozilla.org/file/directory_service;1"].        var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
                       getService(Ci.nsIProperties);                          getService(Ci.nsIProperties);
     var locations = fileLocator.get(NS_APP_SEARCH_DIR_LIST,        var locations = fileLocator.get(NS_APP_SEARCH_DIR_LIST,
                                     Ci.nsISimpleEnumerator);                                        Ci.nsISimpleEnumerator);
 
     while (locations.hasMoreElements()) {        while (locations.hasMoreElements()) {
@@ -2024,33 +2093,70 @@ SearchService.prototype = {   @@ -2059,33 +2128,77 @@ SearchService.prototype = {
 
     } =      }
 
     if (!aEngine.supportsResponseType(URLTYPE_SEARCH_HTML)) {        if (!aEngine.supportsResponseType(URLTYPE_SEARCH_HTML)) {
       LOG("_addEngineToStore: Won't add engines that have no HTML output!");          LOG("_addEngineToStore: Won't add engines that have no HTML output!");
       return;          return;
     }        }
 
-    this._engines[aEngine.name] = aEngine;   -    this._engines[aEngine.name] = aEngine;
-    this._sortedEngines.push(aEngine);   -    this._sortedEngines.push(aEngine);
-    notifyAction(aEngine, SEARCH_ENGINE_ADDED);   -    notifyAction(aEngine, SEARCH_ENGINE_ADDED);
+    if (aEngine._engineToUpdate) {   +    if (aEngine._engineToUpdate) {
+      // We need to replace engineToUpdate with the engine that just loaded.   +      // We need to replace engineToUpdate with the engine that just loaded.
+      var oldEngine = aEngine._engineToUpdate;   +      var oldEngine = aEngine._engineToUpdate;
+   +
  -+ +      // Remove the old engine from the hash, since it's keyed by name, and our
    +      // name might change (the update might have a new name).
    +      delete this._engines[oldEngine.name];
    +
+      // Hack: we want to replace the old engine with the new one, but since = +      // Hack: we want to replace the old engine with the new one, but since
+      // people may be holding refs to the nsISearchEngine objects themselves,   +      // people may be holding refs to the nsISearchEngine objects themselves,
+      // we'll just copy over all "private" properties (those without a getter   +      // we'll just copy over all "private" properties (those without a getter
+      // or setter) from one object to the other.   +      // or setter) from one object to the other.
+      for (var p in aEngine) {   +      for (var p in aEngine) {
+        if (!(aEngine.__lookupGetter__(p) || aEngine.__lookupSetter__(p)))   +        if (!(aEngine.__lookupGetter__(p) || aEngine.__lookupSetter__(p)))
+          oldEngine[p] = aEngine[p];   +          oldEngine[p] = aEngine[p];
+      }   +      }
+      aEngine = oldEngine;   +      aEngine = oldEngine;
+      aEngine._engineToUpdate = null;   +      aEngine._engineToUpdate = null;
  -+ +
    +      // Add the engine back
    +      this._engines[aEngine.name] = aEngine;
+      notifyAction(aEngine, SEARCH_ENGINE_CHANGED); = +      notifyAction(aEngine, SEARCH_ENGINE_CHANGED);
+    } else {   +    } else {
+      // Not an update, just add the new engine <> +      // Not an update, just add the new engine.
+      this._engines[aEngine.name] = aEngine; = +      this._engines[aEngine.name] = aEngine;
+      // Only add the engine to the list of sorted engines if the initial list   +      // 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   +      // 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   +      // it hasn't, we're still loading engines from disk, and will build the
+      // sorted engine list when that initial loading is done.   +      // sorted engine list when that initial loading is done.
+      if (this._sortedEngines)   +      if (this._sortedEngines)
+        this._sortedEngines.push(aEngine);   +        this._sortedEngines.push(aEngine);
+      notifyAction(aEngine, SEARCH_ENGINE_ADDED);   +      notifyAction(aEngine, SEARCH_ENGINE_ADDED);
+    }   +    }
+   +
+    // Schedule the engine's next update, if it isn't already.   +    // Schedule the engine's next update, if it isn't already.
+    if (!engineMetadataService.getAttr(aEngine, "updateexpir"))   +    if (!engineMetadataService.getAttr(aEngine, "updateexpir"))
+      engineUpdateService.scheduleNextUpdate(aEngine);   +      engineUpdateService.scheduleNextUpdate(aEngine);
+   +
 
+    // We need this so that we know how to load any future updates from this = +    // We need this so that we know how to load any future updates from this
+    // engine.   +    // engine.
+    if (!engineMetadataService.getAttr(aEngine, "updatedatatype"))   +    if (!engineMetadataService.getAttr(aEngine, "updatedatatype"))
+      engineMetadataService.setAttr(aEngine, "updatedatatype",   +      engineMetadataService.setAttr(aEngine, "updatedatatype",
+                                    aEngine._dataType);   +                                    aEngine._dataType);
   },      },
 
   _loadEngines: function SRCH_SVC_loadEngines(aDir) {      _loadEngines: function SRCH_SVC_loadEngines(aDir) {
     LOG("_loadEngines: Searching in " + aDir.path + " for search engines.");        LOG("_loadEngines: Searching in " + aDir.path + " for search engines.");
 
     // Check whether aDir is the user profile dir        // Check whether aDir is the user profile dir
     var isInProfile = aDir.equals(getDir(NS_APP_USER_SEARCH_DIR));        var isInProfile = aDir.equals(getDir(NS_APP_USER_SEARCH_DIR));
 
    @@ -2147,24 +2260,24 @@ SearchService.prototype = {
  -+        }
   
           this._addEngineToStore(addedEngine);
         }
       },
   
       _saveSortedEngineList: function SRCH_SVC_saveSortedEngineList() {
         var engines = this._getSortedEngines(false);
    -    var values = [];
    +    var values = [];
         var names = [];
   
    +
         for (var i = 0; i < engines.length; ++i) {
           names[i] = "order";
           values[i] = i + 1;
         }
    -   
    +
         engineMetadataService.setAttrs(engines, names, values);
       },
   
       _buildSortedEngineList: function SRCH_SVC_buildSortedEngineList() {
         var addedEngines = { };
         this._sortedEngines = [];
         var engine;
   
@@ -2150,17 +2256,17 @@ SearchService.prototype = { = @@ -2192,17 +2305,17 @@ SearchService.prototype = {
                                        if (a.name > b.name)                                           if (a.name > b.name)
                                          return 1;                                             return 1;
                                        return 0;                                           return 0;
                                      });                                         });
     this._sortedEngines = this._sortedEngines.concat(alphaEngines);        this._sortedEngines = this._sortedEngines.concat(alphaEngines);
   },      },
 
   /**      /**
-   *  On first startup, there are some default prefs that we need to migrate   -   *  On first startup, there are some default prefs that we need to migrate
+   *  On first startup, there are some default prefs that we need to migrate   +   *  On first startup, there are some default prefs that we need to migrate
    *  into our sqlite database.  This function moves those values, along with       *  into our sqlite database.  This function moves those values, along with
    *  any values users may have set from builds prior to the introduction of       *  any values users may have set from builds prior to the introduction of
    *  the database.       *  the database.
 
     var prefService = Cc["@mozilla.org/preferences-service;1"]. =      var prefService = Cc["@mozilla.org/preferences-service;1"].
                         getService(Ci.nsIPrefService);                            getService(Ci.nsIPrefService);
     for each (engine in this._engines) {        for each (engine in this._engines) {
-      var basePref = BROWSER_SEARCH_PREF + "engine." +   -      var basePref = BROWSER_SEARCH_PREF + "engine." +
+      var basePref = BROWSER_SEARCH_PREF + "engine." +   +      var basePref = BROWSER_SEARCH_PREF + "engine." +
                      encodeURIComponent(engine.name) + ".";                         encodeURIComponent(engine.name) + ".";
 
       var alias = getLocalizedPref(basePref + "alias", "");          var alias = getLocalizedPref(basePref + "alias", "");
       if (alias != "")          if (alias != "")
         engineMetadataService.setAttr(engine, "alias", alias);            engineMetadataService.setAttr(engine, "alias", alias);
 
       var hidden = getBoolPref(basePref + "hidden", false);          var hidden = getBoolPref(basePref + "hidden", false);
       engineMetadataService.setAttr(engine, "hidden", hidden);          engineMetadataService.setAttr(engine, "hidden", hidden);
@@ -2358,21 +2464,21 @@ SearchService.prototype = {   @@ -2398,22 +2511,22 @@ SearchService.prototype = {
  -+             Cr.NS_ERROR_FILE_ALREADY_EXISTS);
   
     var engine = new Engine(getSanitizedFile(aName), SEARCH_DATA_XML, false); =      var engine = new Engine(getSanitizedFile(aName), SEARCH_DATA_XML, false);
     engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,        engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,
                              aMethod, aTemplate);                                 aMethod, aTemplate);
     this._addEngineToStore(engine);        this._addEngineToStore(engine);
   },      },
 
   // If aSelect is true, the newly added engine will be selected as the current <>  
   // engine when it finishes loading.    
-  addEngine: function SRCH_SVC_addEngine(aEngineURL, aType, aIconURL, aSelect) {   -  addEngine: function SRCH_SVC_addEngine(aEngineURL, aType, aIconURL,
+  addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL, aSelect) {   +  addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL,
                                              aConfirm) {
     LOG("addEngine: Adding \"" + aEngineURL + "\"."); =      LOG("addEngine: Adding \"" + aEngineURL + "\".");
     try {        try {
       var uri = makeURI(aEngineURL);          var uri = makeURI(aEngineURL);
-      var engine = new Engine(uri, aType, false);   -      var engine = new Engine(uri, aType, false);
+      var engine = new Engine(uri, aDataType, false);   +      var engine = new Engine(uri, aDataType, false);
       engine._initFromURI();          engine._initFromURI();
+-  
       if (aSelect)    
         this._selectNewEngineURI = uri;    
     } catch (ex) { =      } catch (ex) {
       LOG("addEngine: Error adding engine:\n" + ex);          LOG("addEngine: Error adding engine:\n" + ex);
       throw Cr.NS_ERROR_FAILURE;          throw Cr.NS_ERROR_FAILURE;
     }        }
  -+      engine._setIcon(aIconURL, false);
         engine._confirm = aConfirm;
       },
@@ -2394,16 +2500,17 @@ SearchService.prototype = { = @@ -2433,16 +2546,17 @@ SearchService.prototype = {
       this._currentEngine = null;          this._currentEngine = null;
 
     if (engineToRemove._readOnly) {        if (engineToRemove._readOnly) {
       // Just hide it (the "hidden" setter will notify)          // Just hide it (the "hidden" setter will notify)
       engineToRemove.hidden = true;          engineToRemove.hidden = true;
     } else {        } else {
       // Remove the engine file from disk (this might throw)          // Remove the engine file from disk (this might throw)
       engineToRemove._remove();          engineToRemove._remove();
+      engineToRemove._file = null;   +      engineToRemove._file = null;
 
       // Remove the engine from _sortedEngines          // Remove the engine from _sortedEngines
       var index = this._sortedEngines.indexOf(engineToRemove);          var index = this._sortedEngines.indexOf(engineToRemove);
       ENSURE(index != -1, "Can't find engine to remove in _sortedEngines!",          ENSURE(index != -1, "Can't find engine to remove in _sortedEngines!",
              Cr.NS_ERROR_FAILURE);                 Cr.NS_ERROR_FAILURE);
       this._sortedEngines.splice(index, 1);          this._sortedEngines.splice(index, 1);
 
       // Remove the engine from the internal store          // Remove the engine from the internal store
    @@ -2610,20 +2724,20 @@ var engineMetadataService = {
  -+      pp.engineid = engine._id;
         pp.name = name;
         pp.value = value;
         this.mInsertData.step();
         this.mInsertData.reset();
   
         this.mDB.commitTransaction();
       },
   
    +
       setAttrs: function epsSetAttrs(engines, names, values) {
         this.mDB.beginTransaction();
    -   
    +
         for (var i = 0; i < engines.length; i++) {
           // attr names must be lower case
           var name = names[i].toLowerCase();
   
           var pp = this.mDeleteData.params;
           pp.engineid = engines[i]._id;
           pp.name = names[i];
           this.mDeleteData.step();
@@ -2586,16 +2693,119 @@ var engineMetadataService = { = @@ -2631,32 +2745,135 @@ var engineMetadataService = {
  -+
           pp = this.mInsertData.params;
           pp.engineid = engines[i]._id;
           pp.name = names[i];
           pp.value = values[i];
           this.mInsertData.step();
           this.mInsertData.reset();
         }
    -     
    +
         this.mDB.commitTransaction();
       },
   
       deleteEngineData: function epsDelData(engine, name) {
         // attr names must be lower case
         name = name.toLowerCase();
   
     var pp = this.mDeleteData.params; =      var pp = this.mDeleteData.params;
     pp.engineid = engine._id;        pp.engineid = engine._id;
     pp.name = name;        pp.name = name;
     this.mDeleteData.step();        this.mDeleteData.step();
     this.mDeleteData.reset();        this.mDeleteData.reset();
   }      }
}   }
 
+const SEARCH_UPDATE_LOG_PREFIX = "*** Search update: ";   +const SEARCH_UPDATE_LOG_PREFIX = "*** Search update: ";
+   +
+/**   +/**
+ * Outputs aText to the JavaScript console as well as to stdout, if the search   + * Outputs aText to the JavaScript console as well as to stdout, if the search
+ * logging pref (browser.search.update.log) is set to true.   + * logging pref (browser.search.update.log) is set to true.
+ */   + */