Bug 754265 - Add a dedicated API to provide a final complete value different from the matching one, r=Enn sr=gavin
authorMarco Bonardo <mbonardo@mozilla.com>
Fri, 18 Apr 2014 16:04:19 +0200
changeset 179725 669dff46362f638112c7c910a3c0804c5b9af706
parent 179724 75f108774d93669302358a91e4c9f9f393ea533c
child 179726 db31631ce10ce8afb43e54c7f39bc3a6be92c7fc
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersEnn, gavin
bugs754265
milestone31.0a1
Bug 754265 - Add a dedicated API to provide a final complete value different from the matching one, r=Enn sr=gavin
accessible/tests/mochitest/autocomplete.js
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/nsAutoCompleteController.h
toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp
toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h
toolkit/components/autocomplete/nsIAutoCompleteController.idl
toolkit/components/autocomplete/nsIAutoCompleteResult.idl
toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl
toolkit/components/autocomplete/tests/unit/head_autocomplete.js
toolkit/components/autocomplete/tests/unit/test_378079.js
toolkit/components/autocomplete/tests/unit/test_393191.js
toolkit/components/autocomplete/tests/unit/test_440866.js
toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
toolkit/components/autocomplete/tests/unit/test_previousResult.js
toolkit/components/autocomplete/tests/unit/xpcshell.ini
toolkit/components/filepicker/nsFileView.cpp
toolkit/components/passwordmgr/nsLoginManager.js
toolkit/components/places/nsPlacesAutoComplete.js
toolkit/components/places/nsTaggingService.js
toolkit/components/satchel/nsFormAutoComplete.js
toolkit/components/satchel/nsFormAutoCompleteResult.jsm
toolkit/content/tests/chrome/test_autocomplete2.xul
toolkit/content/tests/chrome/test_autocomplete3.xul
toolkit/content/tests/chrome/test_autocomplete4.xul
toolkit/content/tests/chrome/test_autocomplete5.xul
toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul
toolkit/content/tests/chrome/test_autocomplete_with_composition_on_textbox.xul
--- a/accessible/tests/mochitest/autocomplete.js
+++ b/accessible/tests/mochitest/autocomplete.js
@@ -194,16 +194,21 @@ AutoCompleteResult.prototype =
     return null;
   },
 
   getImageAt: function(aIndex)
   {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex)
+  {
+    return this.getValueAt(aIndex);
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: function(iid) {
     if (iid.equals(nsISupports) ||
         iid.equals(nsIAutoCompleteResult))
       return this;
 
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -405,17 +405,17 @@ nsAutoCompleteController::HandleKeyNavig
 
       if (completeSelection)
       {
         int32_t selectedIndex;
         popup->GetSelectedIndex(&selectedIndex);
         if (selectedIndex >= 0) {
           //  A result is selected, so fill in its value
           nsAutoString value;
-          if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, true, value))) {
+          if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, false, value))) {
             input->SetTextValue(value);
             input->SelectTextRange(value.Length(), value.Length());
           }
         } else {
           // Nothing is selected, so fill in the last typed value
           input->SetTextValue(mSearchString);
           input->SelectTextRange(mSearchString.Length(), mSearchString.Length());
         }
@@ -476,17 +476,17 @@ nsAutoCompleteController::HandleKeyNavig
     if (isOpen) {
       int32_t selectedIndex;
       popup->GetSelectedIndex(&selectedIndex);
       bool shouldComplete;
       input->GetCompleteDefaultIndex(&shouldComplete);
       if (selectedIndex >= 0) {
         // The pop-up is open and has a selection, take its value
         nsAutoString value;
-        if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, true, value))) {
+        if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, false, value))) {
           input->SetTextValue(value);
           input->SelectTextRange(value.Length(), value.Length());
         }
       }
       else if (shouldComplete) {
         // We usually try to preserve the casing of what user has typed, but
         // if he wants to autocomplete, we will replace the value with the
         // actual autocomplete result.
@@ -574,17 +574,17 @@ nsAutoCompleteController::HandleDelete(b
     // There are still rows in the popup, select the current index again.
     popup->SetSelectedIndex(index);
 
     // Complete to the new current value.
     bool shouldComplete = false;
     input->GetCompleteDefaultIndex(&shouldComplete);
     if (shouldComplete) {
       nsAutoString value;
-      if (NS_SUCCEEDED(GetResultValueAt(index, true, value))) {
+      if (NS_SUCCEEDED(GetResultValueAt(index, false, value))) {
         CompleteValue(value);
       }
     }
 
     // Invalidate the popup.
     popup->Invalidate();
   } else {
     // Nothing left in the popup, clear any pending search timers and
@@ -611,25 +611,25 @@ nsAutoCompleteController::GetResultAt(in
   *aResult = mResults[searchIndex];
   NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::GetValueAt(int32_t aIndex, nsAString & _retval)
 {
-  GetResultLabelAt(aIndex, false, _retval);
+  GetResultLabelAt(aIndex, _retval);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::GetLabelAt(int32_t aIndex, nsAString & _retval)
 {
-  GetResultLabelAt(aIndex, false, _retval);
+  GetResultLabelAt(aIndex, _retval);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::GetCommentAt(int32_t aIndex, nsAString & _retval)
 {
   int32_t rowIndex;
@@ -658,16 +658,28 @@ nsAutoCompleteController::GetImageAt(int
   nsIAutoCompleteResult* result;
   nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return result->GetImageAt(rowIndex, _retval);
 }
 
 NS_IMETHODIMP
+nsAutoCompleteController::GetFinalCompleteValueAt(int32_t aIndex,
+                                                  nsAString & _retval)
+{
+  int32_t rowIndex;
+  nsIAutoCompleteResult* result;
+  nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return result->GetFinalCompleteValueAt(rowIndex, _retval);
+}
+
+NS_IMETHODIMP
 nsAutoCompleteController::SetSearchString(const nsAString &aSearchString)
 {
   mSearchString = aSearchString;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::GetSearchString(nsAString &aSearchString)
@@ -1176,19 +1188,24 @@ nsAutoCompleteController::EnterMatch(boo
 
     int32_t selectedIndex;
     popup->GetSelectedIndex(&selectedIndex);
     if (selectedIndex >= 0) {
       // If completeselectedindex is false or a row was selected from the popup,
       // enter it into the textbox. If completeselectedindex is true, or
       // EnterMatch was called via other means, for instance pressing Enter,
       // don't fill in the value as it will have already been filled in as
-      // needed.
-      if (!completeSelection || aIsPopupSelection)
-        GetResultValueAt(selectedIndex, true, value);
+      // needed, unless the final complete value differs.
+      nsAutoString finalValue, inputValue;
+      GetResultValueAt(selectedIndex, true, finalValue);
+      input->GetTextValue(inputValue);
+      if (!completeSelection || aIsPopupSelection ||
+          !finalValue.Equals(inputValue)) {
+        value = finalValue;
+      }
     }
     else if (shouldComplete) {
       // We usually try to preserve the casing of what user has typed, but
       // if he wants to autocomplete, we will replace the value with the
       // actual autocomplete result.
       // The user wants explicitely to use that result, so this ensures
       // association of the result with the autocompleted text.
       nsAutoString defaultIndexValue;
@@ -1201,17 +1218,17 @@ nsAutoCompleteController::EnterMatch(boo
       // we have to find the first default match and enter it instead
       for (uint32_t i = 0; i < mResults.Length(); ++i) {
         nsIAutoCompleteResult *result = mResults[i];
 
         if (result) {
           int32_t defaultIndex;
           result->GetDefaultIndex(&defaultIndex);
           if (defaultIndex >= 0) {
-            result->GetValueAt(defaultIndex, value);
+            result->GetFinalCompleteValueAt(defaultIndex, value);
             break;
           }
         }
       }
     }
   }
 
   nsCOMPtr<nsIObserverService> obsSvc =
@@ -1544,28 +1561,20 @@ nsAutoCompleteController::GetFinalDefaul
 
   result->GetValueAt(defaultIndex, _retval);
   nsAutoString inputValue;
   mInput->GetTextValue(inputValue);
   if (!_retval.Equals(inputValue, nsCaseInsensitiveStringComparator())) {
     return NS_ERROR_FAILURE;
   }
 
-  // Hack: For typeAheadResults allow the comment to be used as the final
-  // defaultComplete value if provided, otherwise fall back to the usual
-  // value.  This allows to provide a different complete text when the user
-  // confirms the match.  Don't rely on this for production code, since it's a
-  // temporary solution that needs a dedicated API (bug 754265).
-  bool isTypeAheadResult = false;
-  nsAutoString commentValue;
-  if (NS_SUCCEEDED(result->GetTypeAheadResult(&isTypeAheadResult)) &&
-      isTypeAheadResult &&
-      NS_SUCCEEDED(result->GetCommentAt(defaultIndex, commentValue)) &&
-      !commentValue.IsEmpty()) {
-    _retval = commentValue;
+  nsAutoString finalCompleteValue;
+  rv = result->GetFinalCompleteValueAt(defaultIndex, finalCompleteValue);
+  if (NS_SUCCEEDED(rv)) {
+    _retval = finalCompleteValue;
   }
 
   MOZ_ASSERT(FindInReadable(inputValue, _retval, nsCaseInsensitiveStringComparator()),
              "Return value must include input value.");
   return NS_OK;
 }
 
 nsresult
@@ -1620,48 +1629,53 @@ nsAutoCompleteController::CompleteValue(
   }
 
   mInput->SelectTextRange(mSearchStringLength, endSelect);
 
   return NS_OK;
 }
 
 nsresult
-nsAutoCompleteController::GetResultLabelAt(int32_t aIndex, bool aValueOnly, nsAString & _retval)
+nsAutoCompleteController::GetResultLabelAt(int32_t aIndex, nsAString & _retval)
 {
-  return GetResultValueLabelAt(aIndex, aValueOnly, false, _retval);
+  return GetResultValueLabelAt(aIndex, false, false, _retval);
 }
 
 nsresult
-nsAutoCompleteController::GetResultValueAt(int32_t aIndex, bool aValueOnly, nsAString & _retval)
+nsAutoCompleteController::GetResultValueAt(int32_t aIndex, bool aGetFinalValue,
+                                           nsAString & _retval)
 {
-  return GetResultValueLabelAt(aIndex, aValueOnly, true, _retval);
+  return GetResultValueLabelAt(aIndex, aGetFinalValue, true, _retval);
 }
 
 nsresult
-nsAutoCompleteController::GetResultValueLabelAt(int32_t aIndex, bool aValueOnly,
-                                                bool aGetValue, nsAString & _retval)
+nsAutoCompleteController::GetResultValueLabelAt(int32_t aIndex,
+                                                bool aGetFinalValue,
+                                                bool aGetValue,
+                                                nsAString & _retval)
 {
   NS_ENSURE_TRUE(aIndex >= 0 && (uint32_t) aIndex < mRowCount, NS_ERROR_ILLEGAL_VALUE);
 
   int32_t rowIndex;
   nsIAutoCompleteResult *result;
   nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint16_t searchResult;
   result->GetSearchResult(&searchResult);
 
   if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) {
-    if (aValueOnly)
+    if (aGetValue)
       return NS_ERROR_FAILURE;
     result->GetErrorDescription(_retval);
   } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS ||
              searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) {
-    if (aGetValue)
+    if (aGetFinalValue)
+      result->GetFinalCompleteValueAt(rowIndex, _retval);
+    else if (aGetValue)
       result->GetValueAt(rowIndex, _retval);
     else
       result->GetLabelAt(rowIndex, _retval);
   }
 
   return NS_OK;
 }
 
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -54,22 +54,21 @@ protected:
   nsresult EnterMatch(bool aIsPopupSelection);
   nsresult RevertTextValue();
 
   nsresult CompleteDefaultIndex(int32_t aResultIndex);
   nsresult CompleteValue(nsString &aValue);
 
   nsresult GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aResult,
                        int32_t* aRowIndex);
-  nsresult GetResultValueAt(int32_t aIndex, bool aValueOnly,
+  nsresult GetResultValueAt(int32_t aIndex, bool aGetFinalValue,
                             nsAString & _retval);
-  nsresult GetResultLabelAt(int32_t aIndex, bool aValueOnly,
-                            nsAString & _retval);
+  nsresult GetResultLabelAt(int32_t aIndex, nsAString & _retval);
 private:
-  nsresult GetResultValueLabelAt(int32_t aIndex, bool aValueOnly,
+  nsresult GetResultValueLabelAt(int32_t aIndex, bool aGetFinalValue,
                                  bool aGetValue, nsAString & _retval);
 protected:
 
   /**
    * Gets and validates the defaultComplete result and the relative
    * defaultIndex value.
    *
    * @param aResultIndex
--- a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp
@@ -85,17 +85,18 @@ nsAutoCompleteSimpleResult::SetTypeAhead
   mTypeAheadResult = aTypeAheadResult;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteSimpleResult::AppendMatch(const nsAString& aValue,
                                         const nsAString& aComment,
                                         const nsAString& aImage,
-                                        const nsAString& aStyle)
+                                        const nsAString& aStyle,
+                                        const nsAString& aFinalCompleteValue)
 {
   CheckInvariants();
 
   if (! mValues.AppendElement(aValue))
     return NS_ERROR_OUT_OF_MEMORY;
   if (! mComments.AppendElement(aComment)) {
     mValues.RemoveElementAt(mValues.Length() - 1);
     return NS_ERROR_OUT_OF_MEMORY;
@@ -106,16 +107,23 @@ nsAutoCompleteSimpleResult::AppendMatch(
     return NS_ERROR_OUT_OF_MEMORY;
   }
   if (! mStyles.AppendElement(aStyle)) {
     mValues.RemoveElementAt(mValues.Length() - 1);
     mComments.RemoveElementAt(mComments.Length() - 1);
     mImages.RemoveElementAt(mImages.Length() - 1);
     return NS_ERROR_OUT_OF_MEMORY;
   }
+  if (!mFinalCompleteValues.AppendElement(aFinalCompleteValue)) {
+    mValues.RemoveElementAt(mValues.Length() - 1);
+    mComments.RemoveElementAt(mComments.Length() - 1);
+    mImages.RemoveElementAt(mImages.Length() - 1);
+    mStyles.RemoveElementAt(mStyles.Length() - 1);
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteSimpleResult::GetMatchCount(uint32_t *aMatchCount)
 {
   CheckInvariants();
 
@@ -166,16 +174,29 @@ nsAutoCompleteSimpleResult::GetStyleAt(i
   NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mStyles.Length()),
                  NS_ERROR_ILLEGAL_VALUE);
   CheckInvariants();
   _retval = mStyles[aIndex];
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsAutoCompleteSimpleResult::GetFinalCompleteValueAt(int32_t aIndex,
+                                                    nsAString& _retval)
+{
+  NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mFinalCompleteValues.Length()),
+                 NS_ERROR_ILLEGAL_VALUE);
+  CheckInvariants();
+  _retval = mFinalCompleteValues[aIndex];
+  if (_retval.Length() == 0)
+    _retval = mValues[aIndex];
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsAutoCompleteSimpleResult::SetListener(nsIAutoCompleteSimpleResultListener* aListener)
 {
   mListener = aListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteSimpleResult::RemoveValueAt(int32_t aRowIndex,
@@ -184,14 +205,15 @@ nsAutoCompleteSimpleResult::RemoveValueA
   NS_ENSURE_TRUE(aRowIndex >= 0 && aRowIndex < int32_t(mValues.Length()),
                  NS_ERROR_ILLEGAL_VALUE);
 
   nsAutoString removedValue(mValues[aRowIndex]);
   mValues.RemoveElementAt(aRowIndex);
   mComments.RemoveElementAt(aRowIndex);
   mImages.RemoveElementAt(aRowIndex);
   mStyles.RemoveElementAt(aRowIndex);
+  mFinalCompleteValues.RemoveElementAt(aRowIndex);
 
   if (mListener)
     mListener->OnValueRemoved(this, removedValue, aRemoveFromDb);
 
   return NS_OK;
 }
--- a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h
@@ -16,16 +16,17 @@
 class nsAutoCompleteSimpleResult MOZ_FINAL : public nsIAutoCompleteSimpleResult
 {
 public:
   nsAutoCompleteSimpleResult();
   inline void CheckInvariants() {
     NS_ASSERTION(mValues.Length() == mComments.Length(), "Arrays out of sync");
     NS_ASSERTION(mValues.Length() == mImages.Length(),   "Arrays out of sync");
     NS_ASSERTION(mValues.Length() == mStyles.Length(),   "Arrays out of sync");
+    NS_ASSERTION(mValues.Length() == mFinalCompleteValues.Length(), "Arrays out of sync");
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAUTOCOMPLETERESULT
   NS_DECL_NSIAUTOCOMPLETESIMPLERESULT
 
 private:
   ~nsAutoCompleteSimpleResult() {}
@@ -34,16 +35,17 @@ protected:
 
   // What we really want is an array of structs with value/comment/image/style contents.
   // But then we'd either have to use COM or manage object lifetimes ourselves.
   // Having four arrays of string simplifies this, but is stupid.
   nsTArray<nsString> mValues;
   nsTArray<nsString> mComments;
   nsTArray<nsString> mImages;
   nsTArray<nsString> mStyles;
+  nsTArray<nsString> mFinalCompleteValues;
 
   nsString mSearchString;
   nsString mErrorDescription;
   int32_t mDefaultIndex;
   uint32_t mSearchResult;
 
   bool mTypeAheadResult;
 
--- a/toolkit/components/autocomplete/nsIAutoCompleteController.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteController.idl
@@ -1,17 +1,17 @@
 /* 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIAutoCompleteInput;
 
-[scriptable, uuid(dd2c4489-e4bd-4702-86bc-e1691744e556)]
+[scriptable, uuid(ff9f8465-204a-47a6-b3c9-0628b3856684)]
 interface nsIAutoCompleteController : nsISupports
 {
   /*
    * Possible values for the searchStatus attribute
    */
   const unsigned short STATUS_NONE = 1;
   const unsigned short STATUS_SEARCHING = 2;
   const unsigned short STATUS_COMPLETE_NO_MATCH = 3;
@@ -130,12 +130,18 @@ interface nsIAutoCompleteController : ns
   AString getStyleAt(in long index);
 
   /*
    * Get the url of the image of the result at a given index in the last completed search
    */
   AString getImageAt(in long index);
 
   /*
+   * For the last completed search, get the final value that should be completed
+   * when the user confirms the match at the given index
+   */
+  AString getFinalCompleteValueAt(in long index);
+
+  /*
    * Get / set the current search string.  Note, setting will not start searching
    */
   attribute AString searchString;
 };
--- a/toolkit/components/autocomplete/nsIAutoCompleteResult.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteResult.idl
@@ -1,15 +1,15 @@
 /* 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/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(7b43fad1-c735-4b45-9383-c3f057fed20d)]
+[scriptable, uuid(9203c031-c4e7-4537-a4ec-81443d623d5a)]
 interface nsIAutoCompleteResult : nsISupports
 {
   /**
    * Possible values for the searchResult attribute
    */
   const unsigned short RESULT_IGNORED = 1; /* indicates invalid searchString */
   const unsigned short RESULT_FAILURE = 2; /* indicates failure */
   const unsigned short RESULT_NOMATCH = 3; /* indicates success with no matches
@@ -77,14 +77,20 @@ interface nsIAutoCompleteResult : nsISup
   AString getStyleAt(in long index);
 
   /**
    * Get the image of the result at the given index
    */
   AString getImageAt(in long index);
 
   /**
+   * Get the final value that should be completed when the user confirms
+   * the match at the given index.
+   */
+  AString getFinalCompleteValueAt(in long index);
+
+  /**
    * Remove the value at the given index from the autocomplete results.
    * If removeFromDb is set to true, the value should be removed from
    * persistent storage as well.
    */
   void removeValueAt(in long rowIndex, in boolean removeFromDb);
 };
--- a/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl
@@ -9,17 +9,17 @@ interface nsIAutoCompleteSimpleResultLis
 
 /**
  * This class implements nsIAutoCompleteResult and provides simple methods
  * for setting the value and result items. It can be used whenever some basic
  * auto complete results are needed that can be pre-generated and filled into
  * an array.
  */
 
-[scriptable, uuid(c738dc26-aa71-4561-a3fd-b5a0e4aa80d2)]
+[scriptable, uuid(fe8802f9-c2b7-4141-8e5b-280df3f62251)]
 interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult
 {
   /**
    * A writer for the readonly attribute 'searchString' which should contain
    * the string that the user typed.
    */
   void setSearchString(in AString aSearchString);
 
@@ -43,22 +43,35 @@ interface nsIAutoCompleteSimpleResult : 
 
   /**
    * A writer for the readonly attribute 'typeAheadResult', typically set
    * because a result is only intended for type-ahead completion.
    */
   void setTypeAheadResult(in boolean aHidden);
 
   /**
-   * Appends a result item consisting of the given value, comment, image and style.
-   * This is how you add results.  Note:  image and style are optional. 
+   * Appends a match consisting of the given value, comment, image, style and
+   * the value to use for defaultIndex completion.
+   * @param aValue
+   *        The value to autocomplete to
+   * @param aComment
+   *        Comment shown in the autocomplete widget to describe this match
+   * @param aImage
+   *        Image shown in the autocomplete widget for this match.
+   * @param aStyle
+   *        Describes how to style the match in the autocomplete widget
+   * @param aFinalCompleteValue
+   *        Value used when the user confirms selecting this match. If not
+   *        provided, aValue will be used.
    */
-  void appendMatch(in AString aValue, in AString aComment, 
-                   [optional] in AString aImage, 
-                   [optional] in AString aStyle);
+  void appendMatch(in AString aValue,
+                   in AString aComment,
+                   [optional] in AString aImage,
+                   [optional] in AString aStyle,
+                   [optional] in AString aFinalCompleteValue);
 
   /**
    * Sets a listener for changes in the result.
    */
   void setListener(in nsIAutoCompleteSimpleResultListener aListener);
 };
 
 [scriptable, uuid(004efdc5-1989-4874-8a7a-345bf2fa33af)]
--- a/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
+++ b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
@@ -73,17 +73,18 @@ function AutoCompleteResultBase(aValues)
   this._values = aValues;
 }
 AutoCompleteResultBase.prototype = {
   
   // Arrays
   _values: null,
   _comments: [],
   _styles: [],
-  
+  _finalCompleteValues: [],
+
   searchString: "",
   searchResult: null,
   
   defaultIndex: -1,
   
   _typeAheadResult: false,
   get typeAheadResult() {
     return this._typeAheadResult;
@@ -108,16 +109,20 @@ AutoCompleteResultBase.prototype = {
   getStyleAt: function(aIndex) {
     return this._styles[aIndex];
   },
   
   getImageAt: function(aIndex) {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex) {
+    return this._finalCompleteValues[aIndex] || this._values[aIndex];
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult])
 }
 
 /** 
  * nsIAutoCompleteSearch implementation that always returns
--- a/toolkit/components/autocomplete/tests/unit/test_378079.js
+++ b/toolkit/components/autocomplete/tests/unit/test_378079.js
@@ -118,16 +118,20 @@ AutoCompleteResult.prototype = {
   getStyleAt: function(aIndex) {
     return this._styles[aIndex];
   },
   
   getImageAt: function(aIndex) {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex) {
+    return this.getValueAt(aIndex);
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIAutoCompleteResult))
       return this;
 
--- a/toolkit/components/autocomplete/tests/unit/test_393191.js
+++ b/toolkit/components/autocomplete/tests/unit/test_393191.js
@@ -117,16 +117,20 @@ AutoCompleteResult.prototype = {
   getStyleAt: function(aIndex) {
     return this._styles[aIndex];
   },
   
   getImageAt: function(aIndex) {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex) {
+    return this.getValueAt(aIndex);
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIAutoCompleteResult))
       return this;
 
--- a/toolkit/components/autocomplete/tests/unit/test_440866.js
+++ b/toolkit/components/autocomplete/tests/unit/test_440866.js
@@ -116,16 +116,20 @@ AutoCompleteResult.prototype = {
   getStyleAt: function(aIndex) {
     return this._styles[aIndex];
   },
 
   getImageAt: function(aIndex) {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex) {
+    return this.getValueAt(aIndex);
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIAutoCompleteResult))
       return this;
 
--- a/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
@@ -105,16 +105,20 @@ AutoCompleteResult.prototype = {
   getStyleAt: function(aIndex) {
     return this._styles[aIndex];
   },
   
   getImageAt: function(aIndex) {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex) {
+    return this.getValueAt(aIndex);
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIAutoCompleteResult))
       return this;
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
@@ -0,0 +1,54 @@
+/* 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 AutoCompleteResult(aValues, aFinalCompleteValues) {
+  this._values = aValues;
+  this._finalCompleteValues = aFinalCompleteValues;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+  this.searches = aSearches;
+  this.popup.selectedIndex = 0;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+function run_test() {
+  run_next_test();
+}
+
+add_test(function test_handleEnter() {
+  doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
+    do_check_eq(aController.input.textValue, "moz");
+    do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
+    aController.handleEnter(false);
+    do_check_eq(aController.input.textValue, "http://www.mozilla.com");
+  });
+});
+
+function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
+  let search = new AutoCompleteSearchBase(
+    "search",
+    new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
+  );
+  registerAutoCompleteSearch(search);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+  
+  // Make an AutoCompleteInput that uses our searches and confirms results.
+  let input = new AutoCompleteInput([ search.name ]);
+  input.textValue = aSearchString;
+
+  controller.input = input;
+  controller.startSearch(aSearchString);
+
+  input.onSearchComplete = function onSearchComplete() {
+    aOnCompleteCallback(controller);
+
+    // Clean up.
+    unregisterAutoCompleteSearch(search);
+    run_next_test();
+  };
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
@@ -0,0 +1,56 @@
+/* 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 AutoCompleteResult(aValues, aFinalCompleteValues) {
+  this._values = aValues;
+  this._finalCompleteValues = aFinalCompleteValues;
+  this.defaultIndex = 0;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+  this.searches = aSearches;
+  this.popup.selectedIndex = -1;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+function run_test() {
+  run_next_test();
+}
+
+add_test(function test_handleEnter() {
+  doSearch("", "mozilla.com", "http://www.mozilla.com", function(aController) {
+    do_check_eq(aController.input.textValue, "");
+    do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
+    aController.input.forceComplete = true;
+    aController.handleEnter(false);
+    do_check_eq(aController.input.textValue, "http://www.mozilla.com");
+  });
+});
+
+function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
+  let search = new AutoCompleteSearchBase(
+    "search",
+    new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
+  );
+  registerAutoCompleteSearch(search);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+  
+  // Make an AutoCompleteInput that uses our searches and confirms results.
+  let input = new AutoCompleteInput([ search.name ]);
+  input.textValue = aSearchString;
+
+  controller.input = input;
+  controller.startSearch(aSearchString);
+
+  input.onSearchComplete = function onSearchComplete() {
+    aOnCompleteCallback(controller);
+
+    // Clean up.
+    unregisterAutoCompleteSearch(search);
+    run_next_test();
+  };
+}
--- a/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
+++ b/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
@@ -1,17 +1,16 @@
 /* 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 AutoCompleteResult(aValues, aComments) {
+function AutoCompleteResult(aValues, aFinalCompleteValues) {
   this._values = aValues;
-  this._comments = aComments;
+  this._finalCompleteValues = aFinalCompleteValues;
   this.defaultIndex = 0;
-  this._typeAheadResult = true;
 }
 AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
 
 function AutoCompleteInput(aSearches) {
   this.searches = aSearches;
   this.popup.selectedIndex = -1;
   this.completeDefaultIndex = true;
 }
@@ -32,20 +31,20 @@ add_test(function test_keyNavigation() {
 add_test(function test_handleEnter() {
   doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
     do_check_eq(aController.input.textValue, "mozilla.com");
     aController.handleEnter(false);
     do_check_eq(aController.input.textValue, "http://www.mozilla.com");
   });
 });
 
-function doSearch(aSearchString, aResultValue, aCommentValue, aOnCompleteCallback) {
+function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
   let search = new AutoCompleteSearchBase(
     "search",
-    new AutoCompleteResult([ aResultValue ], [ aCommentValue ], 0)
+    new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
   );
   registerAutoCompleteSearch(search);
 
   let controller = Cc["@mozilla.org/autocomplete/controller;1"].
                    getService(Ci.nsIAutoCompleteController);  
   
   // Make an AutoCompleteInput that uses our searches and confirms results.
   let input = new AutoCompleteInput([ search.name ]);
--- a/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
+++ b/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
@@ -1,23 +1,22 @@
 /* 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 AutoCompleteTypeAheadResult(aValues, aComments) {
+function AutoCompleteTypeAheadResult(aValues, aFinalCompleteValues) {
   this._values = aValues;
-  this._comments = aComments;
+  this._finalCompleteValues = aFinalCompleteValues;
   this.defaultIndex = 0;
   this._typeAheadResult = true;
 }
 AutoCompleteTypeAheadResult.prototype = Object.create(AutoCompleteResultBase.prototype);
 
-function AutoCompleteResult(aValues, aComments) {
+function AutoCompleteResult(aValues) {
   this._values = aValues;
-  this._comments = aComments;
 }
 AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
 
 function AutoCompleteInput(aSearches) {
   this.searches = aSearches;
   this.popupOpen = true;
   this.completeDefaultIndex = true;
   this.completeSelectedIndex = true;
@@ -40,17 +39,17 @@ function doSearch(aSearchString, aOnComp
   let typeAheadSearch = new AutoCompleteSearchBase(
     "typeAheadSearch",
     new AutoCompleteTypeAheadResult([ "mozilla.com" ], [ "http://www.mozilla.com" ])
   );
   registerAutoCompleteSearch(typeAheadSearch);
 
   let search = new AutoCompleteSearchBase(
     "search",
-    new AutoCompleteResult([ "mozilla.org" ], [ "http://www.mozilla.org" ])
+    new AutoCompleteResult([ "mozilla.org" ])
   );
   registerAutoCompleteSearch(search);
 
   let controller = Cc["@mozilla.org/autocomplete/controller;1"].
                    getService(Ci.nsIAutoCompleteController);
 
   // Make an AutoCompleteInput that uses our searches and confirms results.
   let input = new AutoCompleteInput([ typeAheadSearch.name, search.name ]);
--- a/toolkit/components/autocomplete/tests/unit/test_previousResult.js
+++ b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
@@ -116,16 +116,20 @@ AutoCompleteResult.prototype = {
   getStyleAt: function(aIndex) {
     return this._styles[aIndex];
   },
   
   getImageAt: function(aIndex) {
     return "";
   },
 
+  getFinalCompleteValueAt: function(aIndex) {
+    return this.getValueAt(aIndex);
+  },
+
   removeValueAt: function (aRowIndex, aRemoveFromDb) {},
 
   // nsISupports implementation
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIAutoCompleteResult))
       return this;
 
--- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
@@ -6,14 +6,16 @@ tail =
 [test_378079.js]
 [test_393191.js]
 [test_440866.js]
 [test_463023.js]
 [test_660156.js]
 [test_autocomplete_multiple.js]
 [test_badDefaultIndex.js]
 [test_completeDefaultIndex_casing.js]
+[test_finalCompleteValue.js]
+[test_finalCompleteValue_forceComplete.js]
 [test_finalDefaultCompleteValue.js]
 [test_hiddenResult.js]
 [test_immediate_search.js]
 [test_popupSelectionVsDefaultCompleteValue.js]
 [test_previousResult.js]
 [test_stopSearch.js]
--- a/toolkit/components/filepicker/nsFileView.cpp
+++ b/toolkit/components/filepicker/nsFileView.cpp
@@ -167,16 +167,21 @@ NS_IMETHODIMP nsFileResult::GetStyleAt(i
   return NS_OK;
 }
 
 NS_IMETHODIMP nsFileResult::GetImageAt(int32_t index, nsAString & aImage)
 {
   aImage.Truncate();
   return NS_OK;
 }
+NS_IMETHODIMP nsFileResult::GetFinalCompleteValueAt(int32_t index,
+                                                    nsAString & aValue)
+{
+  return GetValueAt(index, aValue);
+}
 
 NS_IMETHODIMP nsFileResult::RemoveValueAt(int32_t rowIndex, bool removeFromDb)
 {
   return NS_OK;
 }
 
 class nsFileComplete MOZ_FINAL : public nsIAutoCompleteSearch
 {
--- a/toolkit/components/passwordmgr/nsLoginManager.js
+++ b/toolkit/components/passwordmgr/nsLoginManager.js
@@ -606,16 +606,20 @@ UserAutoCompleteResult.prototype = {
     getStyleAt : function (index) {
         return "";
     },
 
     getImageAt : function (index) {
         return "";
     },
 
+    getFinalCompleteValueAt : function (index) {
+        return this.getValueAt(index);
+    },
+
     removeValueAt : function (index, removeFromDB) {
         if (index < 0 || index >= this.logins.length)
             throw "Index out of range.";
 
         var [removedLogin] = this.logins.splice(index, 1);
 
         this.matchCount--;
         if (this.defaultIndex > this.logins.length)
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -1399,19 +1399,17 @@ urlInlineComplete.prototype = {
         let untrimmedHost = row.getResultByIndex(1);
         // If the untrimmed value doesn't preserve the user's input just
         // ignore it and complete to the found host.
         if (untrimmedHost &&
             !untrimmedHost.toLowerCase().contains(ac._originalSearchString.toLowerCase())) {
           untrimmedHost = null;
         }
 
-        // TODO (bug 754265): this is a temporary solution introduced while
-        // waiting for a propert dedicated API.
-        ac._result.appendMatch(ac._strippedPrefix + trimmedHost, untrimmedHost);
+        ac._result.appendMatch(ac._strippedPrefix + trimmedHost, "", "", "", untrimmedHost);
 
         // handleCompletion() will cause the result listener to be called, and
         // will display the result in the UI.
       },
 
       handleError: function (aError) {
         Components.utils.reportError(
           "URL Inline Complete: An async statement encountered an " +
@@ -1475,19 +1473,17 @@ urlInlineComplete.prototype = {
         // If the untrimmed value doesn't preserve the user's input just
         // ignore it and complete to the found url.
         let untrimmedURL = prefix + url;
         if (untrimmedURL &&
             !untrimmedURL.toLowerCase().contains(ac._originalSearchString.toLowerCase())) {
           untrimmedURL = null;
          }
 
-        // TODO (bug 754265): this is a temporary solution introduced while
-        // waiting for a propert dedicated API.
-        ac._result.appendMatch(ac._strippedPrefix + url, untrimmedURL);
+        ac._result.appendMatch(ac._strippedPrefix + url, "", "", "", untrimmedURL);
 
         // handleCompletion() will cause the result listener to be called, and
         // will display the result in the UI.
       },
 
       handleError: function(aError) {
         Components.utils.reportError(
           "URL Inline Complete: An async statement encountered an " +
--- a/toolkit/components/places/nsTaggingService.js
+++ b/toolkit/components/places/nsTaggingService.js
@@ -538,16 +538,23 @@ TagAutoCompleteResult.prototype = {
   /**
    * Get the image for the result at the given index
    */
   getImageAt: function PTACR_getImageAt(index) {
     return null;
   },
 
   /**
+   * Get the image for the result at the given index
+   */
+  getFinalCompleteValueAt: function PTACR_getFinalCompleteValueAt(index) {
+    return this.getValueAt(index);
+  },
+
+  /**
    * Remove the value at the given index from the autocomplete results.
    * If removeFromDb is set to true, the value should be removed from
    * persistent storage as well.
    */
   removeValueAt: function PTACR_removeValueAt(index, removeFromDb) {
     this._results.splice(index, 1);
     this._comments.splice(index, 1);
   },
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -471,16 +471,20 @@ FormAutoCompleteResult.prototype = {
         return "";
     },
 
     getImageAt : function (index) {
         this._checkIndexBounds(index);
         return "";
     },
 
+    getFinalCompleteValueAt : function (index) {
+        return this.getValueAt(index);
+    },
+
     removeValueAt : function (index, removeFromDB) {
         this._checkIndexBounds(index);
 
         let [removedEntry] = this.entries.splice(index, 1);
 
         if (removeFromDB) {
           this.formHistory.update({ op: "remove",
                                     fieldname: this.fieldName,
--- a/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
+++ b/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
@@ -147,16 +147,25 @@ FormAutoCompleteResult.prototype = {
    * @return          the image url at the specified index
    */
   getImageAt: function(index) {
     this._checkIndexBounds(index);
     return "";
   },
 
   /**
+   * Retrieves a result
+   * @param  index    the index of the result requested
+   * @return          the result at the specified index
+   */
+  getFinalCompleteValueAt: function(index) {
+    return this.getValueAt(index);
+  },
+
+  /**
    * Removes a result from the resultset
    * @param  index    the index of the result to remove
    */
   removeValueAt: function(index, removeFromDatabase) {
     this._checkIndexBounds(index);
     // Forward the removeValueAt call to the underlying result if we have one
     // Note: this assumes that the form history results were added to the top
     // of our arrays.
--- a/toolkit/content/tests/chrome/test_autocomplete2.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete2.xul
@@ -39,16 +39,17 @@ nsAutoCompleteSimpleResult.prototype = {
  searchResult: ACR.RESULT_FAILURE,
  defaultIndex: -1,
  errorDescription: null,
  matchCount: 0,
  getValueAt: function() { return this._param; },
  getCommentAt: function() { return null; },
  getStyleAt: function() { return null; },
  getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function() { return this.getValueAt(); },
  getLabelAt: function() { return null; },
  removeValueAt: function() {}
 };
 
 // A basic autocomplete implementation that either returns one result or none
 var autoCompleteSimpleID = Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca");
 var autoCompleteSimpleName = "@mozilla.org/autocomplete/search;1?name=simple"
 var autoCompleteSimple = {
--- a/toolkit/content/tests/chrome/test_autocomplete3.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete3.xul
@@ -39,16 +39,17 @@ nsAutoCompleteSimpleResult.prototype = {
  searchResult: ACR.RESULT_FAILURE,
  defaultIndex: 0,
  errorDescription: null,
  matchCount: 0,
  getValueAt: function() { return this._param; },
  getCommentAt: function() { return null; },
  getStyleAt: function() { return null; },
  getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function() { return this.getValueAt(); },
  getLabelAt: function() { return null; },
  removeValueAt: function() {}
 };
 
 // A basic autocomplete implementation that either returns one result or none
 var autoCompleteSimpleID = Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca");
 var autoCompleteSimpleName = "@mozilla.org/autocomplete/search;1?name=simple"
 var autoCompleteSimple = {
--- a/toolkit/content/tests/chrome/test_autocomplete4.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete4.xul
@@ -41,16 +41,17 @@ nsAutoCompleteSimpleResult.prototype = {
  searchResult: ACR.RESULT_FAILURE,
  defaultIndex: 0,
  errorDescription: null,
  matchCount: 0,
  getValueAt: function() { return this._param; },
  getCommentAt: function() { return null; },
  getStyleAt: function() { return null; },
  getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function() { return this.getValueAt(); },
  getLabelAt: function() { return null; },
  removeValueAt: function() {}
 };
 
 // A basic autocomplete implementation that either returns one result or none
 var autoCompleteSimpleID = Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca");
 var autoCompleteSimpleName = "@mozilla.org/autocomplete/search;1?name=simple"
 var autoCompleteSimple = {
--- a/toolkit/content/tests/chrome/test_autocomplete5.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete5.xul
@@ -37,16 +37,17 @@ nsAutoCompleteSimpleResult.prototype = {
  searchResult: ACR.RESULT_FAILURE,
  defaultIndex: -1,
  errorDescription: null,
  matchCount: 0,
  getValueAt: function() { return this._param; },
  getCommentAt: function() { return null; },
  getStyleAt: function() { return null; },
  getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function() { return this.getValueAt(); },
  getLabelAt: function() { return null; },
  removeValueAt: function() {}
 };
 
 // A basic autocomplete implementation that either returns one result or none
 var autoCompleteSimpleID = Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca");
 var autoCompleteSimpleName = "@mozilla.org/autocomplete/search;1?name=simple"
 var autoCompleteSimple = {
--- a/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul
@@ -37,16 +37,17 @@ autoCompleteSimpleResult.prototype = {
  searchResult: Components.interfaces.nsIAutoCompleteResult.RESULT_FAILURE,
  defaultIndex: 0,
  errorDescription: null,
  matchCount: 0,
  getValueAt: function() { return this._param; },
  getCommentAt: function() { return null; },
  getStyleAt: function() { return null; },
  getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function() { return this.getValueAt(); },
  getLabelAt: function() { return null; },
  removeValueAt: function() {}
 };
 
 // A basic autocomplete implementation that returns one result.
 let autoCompleteSimple = {
   classID: Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca"),
   contractID: "@mozilla.org/autocomplete/search;1?name=simple",
--- a/toolkit/content/tests/chrome/test_autocomplete_with_composition_on_textbox.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete_with_composition_on_textbox.xul
@@ -52,16 +52,17 @@ nsAutoCompleteSimpleResult.prototype = {
  searchResult: nsIAutoCompleteResult.RESULT_FAILURE,
  defaultIndex: 0,
  errorDescription: null,
  matchCount: 0,
  getValueAt: function(aIndex) { return aIndex == 0 ? this._value : null; },
  getCommentAt: function() { return null; },
  getStyleAt: function() { return null; },
  getImageAt: function() { return null; },
+ getFinalCompleteValueAt: function(aIndex) { return this.getValueAt(aIndex); },
  getLabelAt: function() { return null; },
  removeValueAt: function() {}
 };
 
 // A basic autocomplete implementation that either returns one result or none
 var autoCompleteSimpleID =
   Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca");
 var autoCompleteSimpleName =