--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -21,30 +21,16 @@
#include "mozilla/Services.h"
#include "mozilla/ModuleUtils.h"
#include "mozilla/Unused.h"
static const char *kAutoCompleteSearchCID = "@mozilla.org/autocomplete/search;1?name=";
using namespace mozilla;
-namespace {
-
-void
-SetTextValue(nsIAutoCompleteInput* aInput,
- const nsString& aValue,
- uint16_t aReason) {
- nsresult rv = aInput->SetTextValueWithReason(aValue, aReason);
- if (NS_FAILED(rv)) {
- aInput->SetTextValue(aValue);
- }
-}
-
-} // anon namespace
-
NS_IMPL_CYCLE_COLLECTION_CLASS(nsAutoCompleteController)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAutoCompleteController)
tmp->SetInput(nullptr);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAutoCompleteController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInput)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearches)
@@ -77,16 +63,28 @@ nsAutoCompleteController::nsAutoComplete
{
}
nsAutoCompleteController::~nsAutoCompleteController()
{
SetInput(nullptr);
}
+void
+nsAutoCompleteController::SetValueOfInputTo(const nsString& aValue,
+ uint16_t aReason)
+{
+ mSetValue = aValue;
+ nsCOMPtr<nsIAutoCompleteInput> input(mInput);
+ nsresult rv = input->SetTextValueWithReason(aValue, aReason);
+ if (NS_FAILED(rv)) {
+ input->SetTextValue(aValue);
+ }
+}
+
////////////////////////////////////////////////////////////////////////
//// nsIAutoCompleteController
NS_IMETHODIMP
nsAutoCompleteController::GetSearchStatus(uint16_t *aSearchStatus)
{
*aSearchStatus = mSearchStatus;
return NS_OK;
@@ -144,17 +142,19 @@ nsAutoCompleteController::SetInput(nsIAu
// Nothing more to do if the input was just being set to null.
if (!mInput) {
return NS_OK;
}
nsCOMPtr<nsIAutoCompleteInput> input(mInput);
// Reset the current search string.
- input->GetTextValue(mSearchString);
+ nsAutoString value;
+ input->GetTextValue(value);
+ SetSearchStringInternal(value);
// Clear out this reference in case the new input's popup has no tree
mTree = nullptr;
// Initialize our list of search objects
uint32_t searchCount;
input->GetSearchCount(&searchCount);
mResults.SetCapacity(searchCount);
@@ -203,34 +203,34 @@ nsAutoCompleteController::ResetInternalS
{
// Clear out the current search context
if (mInput) {
nsAutoString value;
mInput->GetTextValue(value);
// Stop all searches in case they are async.
Unused << StopSearch();
Unused << ClearResults();
- mSearchString = value;
+ SetSearchStringInternal(value);
}
mPlaceholderCompletionString.Truncate();
mDefaultIndexCompleted = false;
mProhibitAutoFill = false;
mSearchStatus = nsIAutoCompleteController::STATUS_NONE;
mRowCount = 0;
mDelayedRowCountDelta = 0;
mCompletedSelectionIndex = -1;
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteController::StartSearch(const nsAString &aSearchString)
{
- mSearchString = aSearchString;
+ SetSearchStringInternal(aSearchString);
StartSearches();
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteController::HandleText(bool *_retval)
{
*_retval = false;
@@ -316,17 +316,17 @@ nsAutoCompleteController::HandleText(boo
ClearResults();
}
mProhibitAutoFill = true;
mPlaceholderCompletionString.Truncate();
} else {
mProhibitAutoFill = false;
}
- mSearchString = newValue;
+ SetSearchStringInternal(newValue);
// Don't search if the value is empty
if (newValue.Length() == 0) {
// If autocomplete popup was closed by compositionstart event handler,
// we should reopen it forcibly even if the value is empty.
if (popupClosedByCompositionStart && handlingCompositionCommit) {
bool cancel;
HandleKeyNavigation(nsIDOMKeyEvent::DOM_VK_DOWN, &cancel);
@@ -521,31 +521,31 @@ nsAutoCompleteController::HandleKeyNavig
// If the result is the previously autofilled string, then restore
// the search string and selection that existed when the result was
// autofilled. Else, fill the result and move the caret to the end.
int32_t start;
if (value.Equals(mPlaceholderCompletionString,
nsCaseInsensitiveStringComparator())) {
start = mSearchString.Length();
value = mPlaceholderCompletionString;
- SetTextValue(input, value,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
+ SetValueOfInputTo(
+ value, nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
} else {
start = value.Length();
- SetTextValue(input, value,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETESELECTED);
+ SetValueOfInputTo(
+ value, nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETESELECTED);
}
input->SelectTextRange(start, value.Length());
}
mCompletedSelectionIndex = selectedIndex;
} else {
// Nothing is selected, so fill in the last typed value
- SetTextValue(input, mSearchString,
- nsIAutoCompleteInput::TEXTVALUE_REASON_REVERT);
+ SetValueOfInputTo(
+ mSearchString, nsIAutoCompleteInput::TEXTVALUE_REASON_REVERT);
input->SelectTextRange(mSearchString.Length(), mSearchString.Length());
mCompletedSelectionIndex = -1;
}
}
} else {
#ifdef XP_MACOSX
// on Mac, only show the popup if the caret is at the start or end of
// the input and there is no selection, so that the default defined key
@@ -587,17 +587,17 @@ nsAutoCompleteController::HandleKeyNavig
return NS_OK;
}
// Some script may have changed the value of the text field since our
// last keypress or after our focus handler and we don't want to search
// for a stale string.
nsAutoString value;
input->GetTextValue(value);
- mSearchString = value;
+ SetSearchStringInternal(value);
StartSearches();
}
}
}
} else if ( aKey == nsIDOMKeyEvent::DOM_VK_LEFT
|| aKey == nsIDOMKeyEvent::DOM_VK_RIGHT
#ifndef XP_MACOSX
@@ -633,18 +633,18 @@ nsAutoCompleteController::HandleKeyNavig
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, false, value))) {
- SetTextValue(input, value,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETESELECTED);
+ SetValueOfInputTo(
+ value, nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETESELECTED);
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. Note that the autocomplete input can also
// be showing e.g. "bar >> foo bar" if the search matched "bar", a
@@ -659,33 +659,33 @@ nsAutoCompleteController::HandleKeyNavig
int32_t pos = inputValue.Find(" >> ");
if (pos > 0) {
inputValue.Right(suggestedValue, inputValue.Length() - pos - 4);
} else {
suggestedValue = inputValue;
}
if (value.Equals(suggestedValue, nsCaseInsensitiveStringComparator())) {
- SetTextValue(input, value,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
+ SetValueOfInputTo(
+ value, nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
input->SelectTextRange(value.Length(), value.Length());
}
}
}
// Close the pop-up even if nothing was selected
ClearSearchTimer();
ClosePopup();
}
// Update last-searched string to the current input, since the input may
// have changed. Without this, subsequent backspaces look like text
// additions, not text deletions.
nsAutoString value;
input->GetTextValue(value);
- mSearchString = value;
+ SetSearchStringInternal(value);
}
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteController::HandleDelete(bool *_retval)
{
@@ -845,17 +845,17 @@ nsAutoCompleteController::GetFinalComple
NS_ENSURE_SUCCESS(rv, rv);
return result->GetFinalCompleteValueAt(rowIndex, _retval);
}
NS_IMETHODIMP
nsAutoCompleteController::SetSearchString(const nsAString &aSearchString)
{
- mSearchString = aSearchString;
+ SetSearchStringInternal(aSearchString);
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteController::GetSearchString(nsAString &aSearchString)
{
aSearchString = mSearchString;
return NS_OK;
@@ -1569,19 +1569,19 @@ nsAutoCompleteController::EnterMatch(boo
}
}
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
NS_ENSURE_STATE(obsSvc);
obsSvc->NotifyObservers(input, "autocomplete-will-enter-text", nullptr);
if (!value.IsEmpty()) {
- SetTextValue(input, value, nsIAutoCompleteInput::TEXTVALUE_REASON_ENTERMATCH);
+ SetValueOfInputTo(value, nsIAutoCompleteInput::TEXTVALUE_REASON_ENTERMATCH);
input->SelectTextRange(value.Length(), value.Length());
- mSearchString = value;
+ SetSearchStringInternal(value);
}
obsSvc->NotifyObservers(input, "autocomplete-did-enter-text", nullptr);
ClosePopup();
bool cancel;
input->OnTextEntered(aEvent, &cancel);
@@ -1592,33 +1592,41 @@ nsresult
nsAutoCompleteController::RevertTextValue()
{
// 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 #408463 for more details
if (!mInput)
return NS_OK;
- nsAutoString oldValue(mSearchString);
nsCOMPtr<nsIAutoCompleteInput> input(mInput);
+ // If current input value is different from what we have set, it means
+ // somebody modified the value like JS of the web content. In such case,
+ // we shouldn't overwrite it with the old value.
+ nsAutoString currentValue;
+ input->GetTextValue(currentValue);
+ if (currentValue != mSetValue) {
+ SetSearchStringInternal(currentValue);
+ return NS_OK;
+ }
+
bool cancel = false;
input->OnTextReverted(&cancel);
if (!cancel) {
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
NS_ENSURE_STATE(obsSvc);
obsSvc->NotifyObservers(input, "autocomplete-will-revert-text", nullptr);
- nsAutoString inputValue;
- input->GetTextValue(inputValue);
// Don't change the value if it is the same to prevent sending useless events.
// NOTE: how can |RevertTextValue| be called with inputValue != oldValue?
- if (!oldValue.Equals(inputValue)) {
- SetTextValue(input, oldValue, nsIAutoCompleteInput::TEXTVALUE_REASON_REVERT);
+ if (mSearchString != currentValue) {
+ SetValueOfInputTo(
+ mSearchString, nsIAutoCompleteInput::TEXTVALUE_REASON_REVERT);
}
obsSvc->NotifyObservers(input, "autocomplete-did-revert-text", nullptr);
}
return NS_OK;
}
@@ -1947,18 +1955,18 @@ nsAutoCompleteController::CompleteValue(
if (aValue.IsEmpty() ||
StringBeginsWith(aValue, mSearchString,
nsCaseInsensitiveStringComparator())) {
// aValue is empty (we were asked to clear mInput), or mSearchString
// matches the beginning of aValue. In either case we can simply
// autocomplete to aValue.
mPlaceholderCompletionString = aValue;
- SetTextValue(input, aValue,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
+ SetValueOfInputTo(
+ aValue, nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
} else {
nsresult rv;
nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString scheme;
if (NS_SUCCEEDED(ios->ExtractScheme(NS_ConvertUTF16toUTF8(aValue), scheme))) {
// Trying to autocomplete a URI from somewhere other than the beginning.
// Only succeed if the missing portion is "http://"; otherwise do not
@@ -1970,26 +1978,28 @@ nsAutoCompleteController::CompleteValue(
!scheme.LowerCaseEqualsLiteral("http") ||
!Substring(aValue, findIndex, mSearchStringLength).Equals(
mSearchString, nsCaseInsensitiveStringComparator())) {
return NS_OK;
}
mPlaceholderCompletionString = mSearchString +
Substring(aValue, mSearchStringLength + findIndex, endSelect);
- SetTextValue(input, mPlaceholderCompletionString,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
+ SetValueOfInputTo(
+ mPlaceholderCompletionString,
+ nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
endSelect -= findIndex; // We're skipping this many characters of aValue.
} else {
// Autocompleting something other than a URI from the middle.
// Use the format "searchstring >> full string" to indicate to the user
// what we are going to replace their search string with.
- SetTextValue(input, mSearchString + NS_LITERAL_STRING(" >> ") + aValue,
- nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
+ SetValueOfInputTo(
+ mSearchString + NS_LITERAL_STRING(" >> ") + aValue,
+ nsIAutoCompleteInput::TEXTVALUE_REASON_COMPLETEDEFAULT);
endSelect = mSearchString.Length() + 4 + aValue.Length();
// Reset the last search completion.
mPlaceholderCompletionString.Truncate();
}
}