Bug 720792 - Implement a better solution to start selected searches without a timeout.
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 01 Mar 2012 14:37:13 +0100
changeset 88067 91ca0cc06f46dc519ee50403131b7f8fd3e4089d
parent 88066 b92e7499019ef5d97fd483ca84d45d7e6d2485a0
child 88068 b1fd65f6165d07fba297e53207eab49fba929bf6
push id6662
push usermak77@bonardo.net
push dateThu, 01 Mar 2012 13:38:32 +0000
treeherdermozilla-inbound@91ca0cc06f46 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs720792
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 720792 - Implement a better solution to start selected searches without a timeout. r=gavin
browser/base/content/test/Makefile.in
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/nsAutoCompleteController.h
toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
toolkit/components/autocomplete/tests/unit/test_immediate_search.js
toolkit/components/autocomplete/tests/unit/xpcshell.ini
toolkit/components/places/nsPlacesAutoComplete.js
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -88,17 +88,16 @@ endif
 #
 # browser_sanitizeDialog_treeView.js is disabled until the tree view is added
 # back to the clear recent history dialog (sanitize.xul), if it ever is (bug
 # 480169)
 
 # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
 
 # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
-# browser_urlbarAutoFillTrimURLs.js is disabled till bug 720792 is fixed
 
 _BROWSER_FILES = \
                  head.js \
                  browser_typeAheadFind.js \
                  browser_keywordSearch.js \
                  browser_allTabsPanel.js \
                  browser_alltabslistener.js \
                  browser_bug304198.js \
@@ -218,16 +217,17 @@ endif
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
                  browser_tabs_isActive.js \
                  browser_tabs_owner.js \
+                 browser_urlbarAutoFillTrimURLs.js \
                  browser_urlbarCopying.js \
                  browser_urlbarEnter.js \
                  browser_urlbarRevert.js \
                  browser_urlbarTrimURLs.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -80,17 +80,19 @@ nsAutoCompleteController::nsAutoComplete
   mDefaultIndexCompleted(false),
   mBackspaced(false),
   mPopupClosedByCompositionStart(false),
   mIsIMEComposing(false),
   mIgnoreHandleText(false),
   mSearchStatus(nsAutoCompleteController::STATUS_NONE),
   mRowCount(0),
   mSearchesOngoing(0),
-  mFirstSearchResult(false)
+  mSearchesFailed(0),
+  mFirstSearchResult(false),
+  mImmediateSearchesCount(0)
 {
 }
 
 nsAutoCompleteController::~nsAutoCompleteController()
 {
   SetInput(nsnull);
 }
 
@@ -156,40 +158,50 @@ nsAutoCompleteController::SetInput(nsIAu
   mSearchesOngoing = 0;
 
   // Initialize our list of search objects
   PRUint32 searchCount;
   aInput->GetSearchCount(&searchCount);
   mResults.SetCapacity(searchCount);
   mSearches.SetCapacity(searchCount);
   mMatchCounts.SetLength(searchCount);
+  mImmediateSearchesCount = 0;
 
   const char *searchCID = kAutoCompleteSearchCID;
 
   for (PRUint32 i = 0; i < searchCount; ++i) {
     // Use the search name to create the contract id string for the search service
     nsCAutoString searchName;
     aInput->GetSearchAt(i, searchName);
     nsCAutoString cid(searchCID);
     cid.Append(searchName);
 
     // Use the created cid to get a pointer to the search service and store it for later
     nsCOMPtr<nsIAutoCompleteSearch> search = do_GetService(cid.get());
-    if (search)
+    if (search) {
       mSearches.AppendObject(search);
+
+      // Count immediate searches.
+      PRUint16 searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
+      nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
+        do_QueryInterface(search);
+      if (searchDesc && NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) &&
+          searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE)
+        mImmediateSearchesCount++;
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::StartSearch(const nsAString &aSearchString)
 {
   mSearchString = aSearchString;
-  StartSearchTimer();
+  StartSearches();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::HandleText()
 {
   // We should do nothing during composition.
   if (mIsIMEComposing) {
@@ -257,17 +269,17 @@ nsAutoCompleteController::HandleText()
   mSearchString = newValue;
 
   // Don't search if the value is empty
   if (newValue.Length() == 0) {
     ClosePopup();
     return NS_OK;
   }
 
-  StartSearchTimer();
+  StartSearches();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::HandleEnter(bool aIsPopupSelection, bool *_retval)
 {
   *_retval = false;
@@ -483,17 +495,17 @@ nsAutoCompleteController::HandleKeyNavig
 
           if (!mInput) {
             // StopSearch() can call PostSearchCleanup() which might result
             // in a blur event, which could null out mInput, so we need to check it
             // again.  See bug #395344 for more details
             return NS_OK;
           }
 
-          StartSearchTimer();
+          StartSearches();
         }
       }
     }
   } else if (   aKey == nsIDOMKeyEvent::DOM_VK_LEFT
              || aKey == nsIDOMKeyEvent::DOM_VK_RIGHT
 #ifndef XP_MACOSX
              || aKey == nsIDOMKeyEvent::DOM_VK_HOME
 #endif
@@ -722,17 +734,26 @@ nsAutoCompleteController::OnSearchResult
 
 ////////////////////////////////////////////////////////////////////////
 //// nsITimerCallback
 
 NS_IMETHODIMP
 nsAutoCompleteController::Notify(nsITimer *timer)
 {
   mTimer = nsnull;
-  StartSearch();
+
+  if (mImmediateSearchesCount == 0) {
+    // If there were no immediate searches, BeforeSearches has not yet been
+    // called, so do it now.
+    nsresult rv = BeforeSearches();
+    if (NS_FAILED(rv))
+      return rv;
+  }
+  StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED);
+  AfterSearches();
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////
 // nsITreeView
 
 NS_IMETHODIMP
 nsAutoCompleteController::GetRowCount(PRInt32 *aRowCount)
@@ -997,41 +1018,60 @@ nsAutoCompleteController::ClosePopup()
   nsCOMPtr<nsIAutoCompletePopup> popup;
   mInput->GetPopup(getter_AddRefs(popup));
   NS_ENSURE_TRUE(popup != nsnull, NS_ERROR_FAILURE);
   popup->SetSelectedIndex(-1);
   return mInput->SetPopupOpen(false);
 }
 
 nsresult
-nsAutoCompleteController::StartSearch()
+nsAutoCompleteController::BeforeSearches()
 {
   NS_ENSURE_STATE(mInput);
-  nsCOMPtr<nsIAutoCompleteInput> input(mInput);
+
   mSearchStatus = nsIAutoCompleteController::STATUS_SEARCHING;
   mDefaultIndexCompleted = false;
 
-  // Cache the current results so that we can pass these through to all the
-  // searches without losing them
-  nsCOMArray<nsIAutoCompleteResult> resultCache;
-  if (!resultCache.AppendObjects(mResults)) {
+  // The first search result will clear mResults array, though we should pass
+  // the previous result to each search to allow them to reuse it.  So we
+  // temporarily cache current results till AfterSearches().
+  if (!mResultCache.AppendObjects(mResults)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  PRUint32 count = mSearches.Count();
-  mSearchesOngoing = count;
+  mSearchesOngoing = mSearches.Count();
+  mSearchesFailed = 0;
   mFirstSearchResult = true;
 
   // notify the input that the search is beginning
-  input->OnSearchBegin();
+  mInput->OnSearchBegin();
+
+  return NS_OK;
+}
+
+nsresult
+nsAutoCompleteController::StartSearch(PRUint16 aSearchType)
+{
+  NS_ENSURE_STATE(mInput);
+  nsCOMPtr<nsIAutoCompleteInput> input = mInput;
 
-  PRUint32 searchesFailed = 0;
-  for (PRUint32 i = 0; i < count; ++i) {
+  for (PRInt32 i = 0; i < mSearches.Count(); ++i) {
     nsCOMPtr<nsIAutoCompleteSearch> search = mSearches[i];
-    nsIAutoCompleteResult *result = resultCache.SafeObjectAt(i);
+
+    // Filter on search type.  Not all the searches implement this interface,
+    // in such a case just consider them delayed.
+    PRUint16 searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
+    nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
+      do_QueryInterface(search);
+    if (searchDesc)
+      searchDesc->GetSearchType(&searchType);
+    if (searchType != aSearchType)
+      continue;
+
+    nsIAutoCompleteResult *result = mResultCache.SafeObjectAt(i);
 
     if (result) {
       PRUint16 searchResult;
       result->GetSearchResult(&searchResult);
       if (searchResult != nsIAutoCompleteResult::RESULT_SUCCESS &&
           searchResult != nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING &&
           searchResult != nsIAutoCompleteResult::RESULT_NOMATCH)
         result = nsnull;
@@ -1039,34 +1079,39 @@ nsAutoCompleteController::StartSearch()
 
     nsAutoString searchParam;
     nsresult rv = input->GetSearchParam(searchParam);
     if (NS_FAILED(rv))
         return rv;
 
     rv = search->StartSearch(mSearchString, searchParam, result, static_cast<nsIAutoCompleteObserver *>(this));
     if (NS_FAILED(rv)) {
-      ++searchesFailed;
+      ++mSearchesFailed;
       --mSearchesOngoing;
     }
     // Because of the joy of nested event loops (which can easily happen when some
     // code uses a generator for an asynchronous AutoComplete search),
     // nsIAutoCompleteSearch::StartSearch might cause us to be detached from our input
     // field.  The next time we iterate, we'd be touching something that we shouldn't
     // be, and result in a crash.
     if (!mInput) {
       // The search operation has been finished.
       return NS_OK;
     }
   }
 
-  if (searchesFailed == count)
+  return NS_OK;
+}
+
+void
+nsAutoCompleteController::AfterSearches()
+{
+  mResultCache.Clear();
+  if (mSearchesFailed == mSearches.Count())
     PostSearchCleanup();
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::StopSearch()
 {
   // Stop the timer if there is one
   ClearSearchTimer();
 
@@ -1082,27 +1127,55 @@ nsAutoCompleteController::StopSearch()
     // since we were searching, but now we've stopped,
     // we need to call PostSearchCleanup()
     PostSearchCleanup();
   }
   return NS_OK;
 }
 
 nsresult
-nsAutoCompleteController::StartSearchTimer()
+nsAutoCompleteController::StartSearches()
 {
   // Don't create a new search timer if we're already waiting for one to fire.
   // If we don't check for this, we won't be able to cancel the original timer
   // and may crash when it fires (bug 236659).
   if (mTimer || !mInput)
     return NS_OK;
 
+  // Get the timeout for delayed searches.
   PRUint32 timeout;
   mInput->GetTimeout(&timeout);
 
+  PRUint32 immediateSearchesCount = mImmediateSearchesCount;
+  if (timeout == 0) {
+    // All the searches should be executed immediately.
+    immediateSearchesCount = mSearches.Count();
+  }
+
+  if (immediateSearchesCount > 0) {
+    nsresult rv = BeforeSearches();
+    if (NS_FAILED(rv))
+      return rv;
+    StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE);
+
+    if (mSearches.Count() == immediateSearchesCount) {
+      // Either all searches are immediate, or the timeout is 0.  In the
+      // latter case we still have to execute the delayed searches, otherwise
+      // this will be a no-op.
+      StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED);
+
+      // All the searches have been started, just finish.
+      AfterSearches();
+      return NS_OK;
+    }
+  }
+
+  MOZ_ASSERT(timeout > 0, "Trying to delay searches with a 0 timeout!");
+
+  // Now start the delayed searches.
   nsresult rv;
   mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
   if (NS_FAILED(rv))
       return rv;
   rv = mTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv))
       mTimer = nsnull;
 
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -69,19 +69,21 @@ public:
    
   nsAutoCompleteController();
   virtual ~nsAutoCompleteController();
   
 protected:
   nsresult OpenPopup();
   nsresult ClosePopup();
 
-  nsresult StartSearch();
-  
-  nsresult StartSearchTimer();
+  nsresult StartSearch(PRUint16 aSearchType);
+
+  nsresult BeforeSearches();
+  nsresult StartSearches();
+  void AfterSearches();
   nsresult ClearSearchTimer();
 
   nsresult ProcessResult(PRInt32 aSearchIndex, nsIAutoCompleteResult *aResult);
   nsresult PostSearchCleanup();
 
   nsresult EnterMatch(bool aIsPopupSelection);
   nsresult RevertTextValue();
 
@@ -106,27 +108,35 @@ protected:
                             PRInt32 *aSearchIndex, PRInt32 *aItemIndex);
 
   // members //////////////////////////////////////////
   
   nsCOMPtr<nsIAutoCompleteInput> mInput;
 
   nsCOMArray<nsIAutoCompleteSearch> mSearches;
   nsCOMArray<nsIAutoCompleteResult> mResults;
+  // Caches the match counts for the current ongoing results to allow
+  // incremental results to keep the rowcount up to date.
   nsTArray<PRUint32> mMatchCounts;
-  
+  // Temporarily keeps the results alive while invoking startSearch() for each
+  // search.  This is needed to allow the searches to reuse the previous result,
+  // since otherwise the first search clears mResults.
+  nsCOMArray<nsIAutoCompleteResult> mResultCache;
+
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsITreeSelection> mSelection;
   nsCOMPtr<nsITreeBoxObject> mTree;
 
   nsString mSearchString;
   bool mDefaultIndexCompleted;
   bool mBackspaced;
   bool mPopupClosedByCompositionStart;
   bool mIsIMEComposing;
   bool mIgnoreHandleText;
   PRUint16 mSearchStatus;
   PRUint32 mRowCount;
   PRUint32 mSearchesOngoing;
+  PRUint32 mSearchesFailed;
   bool mFirstSearchResult;
+  PRUint32 mImmediateSearchesCount;
 };
 
 #endif /* __nsAutoCompleteController__ */
--- a/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
@@ -77,8 +77,25 @@ interface nsIAutoCompleteObserver : nsIS
   /*
    * Called to update with new results
    *
    * @param search - The search object that processed this search
    * @param result - The search result object
    */
   void onUpdateSearchResult(in nsIAutoCompleteSearch search, in nsIAutoCompleteResult result);
 };
+
+[scriptable, uuid(02314d6e-b730-40cc-a215-221554d77064)]
+interface nsIAutoCompleteSearchDescriptor : nsISupports
+{
+  // The search is started after the timeout specified by the corresponding
+  // nsIAutoCompleteInput implementation.
+  const unsigned short SEARCH_TYPE_DELAYED = 0;
+  // The search is started synchronously, before any delayed searches.
+  const unsigned short SEARCH_TYPE_IMMEDIATE = 1;
+
+  /**
+   * Identifies the search behavior.
+   * Should be one of the SEARCH_TYPE_* constants above.
+   * Defaults to SEARCH_TYPE_DELAYED.
+   */
+  readonly attribute unsigned short searchType;
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_immediate_search.js
@@ -0,0 +1,160 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+function AutoCompleteImmediateSearch(aName, aResult) {
+  this.name = aName;
+  this._result = aResult;
+}
+AutoCompleteImmediateSearch.prototype = Object.create(AutoCompleteSearchBase.prototype);
+AutoCompleteImmediateSearch.prototype.searchType =
+  Ci.nsIAutoCompleteSearchDescriptor.SEARCH_TYPE_IMMEDIATE;
+AutoCompleteImmediateSearch.prototype.QueryInterface =
+  XPCOMUtils.generateQI([Ci.nsIFactory,
+                         Ci.nsIAutoCompleteSearch,
+                         Ci.nsIAutoCompleteSearchDescriptor]);
+
+function AutoCompleteDelayedSearch(aName, aResult) {
+  this.name = aName;
+  this._result = aResult;
+}
+AutoCompleteDelayedSearch.prototype = Object.create(AutoCompleteSearchBase.prototype);
+
+function AutoCompleteResult(aValues, aDefaultIndex) {
+  this._values = aValues;
+  this.defaultIndex = aDefaultIndex;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function run_test() {
+  run_next_test();
+}
+
+/**
+ * An immediate search should be executed synchronously.
+ */
+add_test(function test_immediate_search() {
+  let immediateResults = ["mozillaTest"];
+  let inputStr = "moz";
+
+  let immediateSearch = new AutoCompleteImmediateSearch(
+    "immediate", new AutoCompleteResult(["moz-immediate"], 0));
+  registerAutoCompleteSearch(immediateSearch);
+  let delayedSearch = new AutoCompleteDelayedSearch(
+    "delayed", new AutoCompleteResult(["moz-delayed"], 0));
+  registerAutoCompleteSearch(delayedSearch);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+
+  let input = new AutoCompleteInputBase([delayedSearch.name,
+                                         immediateSearch.name]);
+  input.completeDefaultIndex = true;
+  input.textValue = inputStr;
+
+  // Caret must be at the end. Autofill doesn't happen unless you're typing
+  // characters at the end.
+  let strLen = inputStr.length;
+  input.selectTextRange(strLen, strLen);
+
+  controller.input = input;
+  controller.startSearch(inputStr);
+
+  // Immediately check the result, the immediate search should have finished.
+  do_check_eq(input.textValue, "moz-immediate");
+
+  // Wait for both queries to finish.
+  input.onSearchComplete = function() {
+    // Sanity check.
+    do_check_eq(input.textValue, "moz-immediate");
+
+    unregisterAutoCompleteSearch(immediateSearch);
+    unregisterAutoCompleteSearch(delayedSearch);
+    run_next_test();
+  };
+});
+
+/**
+ * An immediate search should be executed before any delayed search.
+ */
+add_test(function test_immediate_search_notimeout() {
+  let immediateResults = ["mozillaTest"];
+  let inputStr = "moz";
+
+  let immediateSearch = new AutoCompleteImmediateSearch(
+    "immediate", new AutoCompleteResult(["moz-immediate"], 0));
+  registerAutoCompleteSearch(immediateSearch);
+
+  let delayedSearch = new AutoCompleteDelayedSearch(
+    "delayed", new AutoCompleteResult(["moz-delayed"], 0));
+  registerAutoCompleteSearch(delayedSearch);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+
+  let input = new AutoCompleteInputBase([delayedSearch.name,
+                                         immediateSearch.name]);
+  input.completeDefaultIndex = true;
+  input.textValue = inputStr;
+  input.timeout = 0;
+
+  // Caret must be at the end. Autofill doesn't happen unless you're typing
+  // characters at the end.
+  let strLen = inputStr.length;
+  input.selectTextRange(strLen, strLen);
+
+  controller.input = input;
+  let complete = false;
+  input.onSearchComplete = function() {
+    complete = true;
+  };
+  controller.startSearch(inputStr);
+  do_check_true(complete);
+
+  // Immediately check the result, the immediate search should have finished.
+  do_check_eq(input.textValue, "moz-immediate");
+
+  unregisterAutoCompleteSearch(immediateSearch);
+  unregisterAutoCompleteSearch(delayedSearch);
+  run_next_test();
+});
+
+/**
+ * A delayed search should be executed synchronously with a zero timeout.
+ */
+add_test(function test_delayed_search_notimeout() {
+  let immediateResults = ["mozillaTest"];
+  let inputStr = "moz";
+
+  let delayedSearch = new AutoCompleteDelayedSearch(
+    "delayed", new AutoCompleteResult(["moz-delayed"], 0));
+  registerAutoCompleteSearch(delayedSearch);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+
+  let input = new AutoCompleteInputBase([delayedSearch.name]);
+  input.completeDefaultIndex = true;
+  input.textValue = inputStr;
+  input.timeout = 0;
+
+  // Caret must be at the end. Autofill doesn't happen unless you're typing
+  // characters at the end.
+  let strLen = inputStr.length;
+  input.selectTextRange(strLen, strLen);
+
+  controller.input = input;
+  let complete = false;
+  input.onSearchComplete = function() {
+    complete = true;
+  };
+  controller.startSearch(inputStr);
+  do_check_true(complete);
+
+  // Immediately check the result, the delayed search should have finished.
+  do_check_eq(input.textValue, "moz-delayed");
+
+  unregisterAutoCompleteSearch(delayedSearch);
+  run_next_test();
+});
--- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
@@ -7,10 +7,11 @@ tail =
 [test_393191.js]
 [test_440866.js]
 [test_463023.js]
 [test_660156.js]
 [test_autocomplete_multiple.js]
 [test_badDefaultIndex.js]
 [test_completeDefaultIndex_casing.js]
 [test_hiddenResult.js]
+[test_immediate_search.js]
 [test_previousResult.js]
 [test_stopSearch.js]
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -1473,16 +1473,20 @@ urlInlineComplete.prototype = {
                                          kBrowserUrlbarAutofillTypedPref,
                                          true);
     if (aRegisterObserver) {
       Services.prefs.addObserver(kBrowserUrlbarBranch, this, true);
     }
   },
 
   //////////////////////////////////////////////////////////////////////////////
+  //// nsIAutoCompleteSearchDescriptor
+  get searchType() Ci.nsIAutoCompleteSearchDescriptor.SEARCH_TYPE_IMMEDIATE,
+
+  //////////////////////////////////////////////////////////////////////////////
   //// mozIStorageStatementCallback
 
   handleResult: function UIC_handleResult(aResultSet)
   {
     let row = aResultSet.getNextRow();
     let url = fixupSearchText(row.getResultByIndex(0));
 
     // We must complete the URL up to the next separator (which is /, ? or #).
@@ -1604,16 +1608,17 @@ urlInlineComplete.prototype = {
   //// nsISupports
 
   classID: Components.ID("c88fae2d-25cf-4338-a1f4-64a320ea7440"),
 
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(urlInlineComplete),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIAutoCompleteSearch,
+    Ci.nsIAutoCompleteSearchDescriptor,
     Ci.mozIStorageStatementCallback,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
   ])
 };
 
 let components = [nsPlacesAutoComplete, urlInlineComplete];
 const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);