1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1761 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsAutoCompleteController.h" 1.10 +#include "nsAutoCompleteSimpleResult.h" 1.11 + 1.12 +#include "nsAutoPtr.h" 1.13 +#include "nsNetCID.h" 1.14 +#include "nsIIOService.h" 1.15 +#include "nsToolkitCompsCID.h" 1.16 +#include "nsIServiceManager.h" 1.17 +#include "nsIAtomService.h" 1.18 +#include "nsReadableUtils.h" 1.19 +#include "nsUnicharUtils.h" 1.20 +#include "nsITreeBoxObject.h" 1.21 +#include "nsITreeColumns.h" 1.22 +#include "nsIObserverService.h" 1.23 +#include "nsIDOMKeyEvent.h" 1.24 +#include "mozilla/Services.h" 1.25 +#include "mozilla/ModuleUtils.h" 1.26 + 1.27 +static const char *kAutoCompleteSearchCID = "@mozilla.org/autocomplete/search;1?name="; 1.28 + 1.29 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsAutoCompleteController) 1.30 + 1.31 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAutoCompleteController) 1.32 + tmp->SetInput(nullptr); 1.33 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.34 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAutoCompleteController) 1.35 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInput) 1.36 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearches) 1.37 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResults) 1.38 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.39 + 1.40 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAutoCompleteController) 1.41 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAutoCompleteController) 1.42 +NS_INTERFACE_TABLE_HEAD(nsAutoCompleteController) 1.43 + NS_INTERFACE_TABLE(nsAutoCompleteController, nsIAutoCompleteController, 1.44 + nsIAutoCompleteObserver, nsITimerCallback, nsITreeView) 1.45 + NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAutoCompleteController) 1.46 +NS_INTERFACE_MAP_END 1.47 + 1.48 +nsAutoCompleteController::nsAutoCompleteController() : 1.49 + mDefaultIndexCompleted(false), 1.50 + mBackspaced(false), 1.51 + mPopupClosedByCompositionStart(false), 1.52 + mCompositionState(eCompositionState_None), 1.53 + mSearchStatus(nsAutoCompleteController::STATUS_NONE), 1.54 + mRowCount(0), 1.55 + mSearchesOngoing(0), 1.56 + mSearchesFailed(0), 1.57 + mFirstSearchResult(false), 1.58 + mImmediateSearchesCount(0) 1.59 +{ 1.60 +} 1.61 + 1.62 +nsAutoCompleteController::~nsAutoCompleteController() 1.63 +{ 1.64 + SetInput(nullptr); 1.65 +} 1.66 + 1.67 +//////////////////////////////////////////////////////////////////////// 1.68 +//// nsIAutoCompleteController 1.69 + 1.70 +NS_IMETHODIMP 1.71 +nsAutoCompleteController::GetSearchStatus(uint16_t *aSearchStatus) 1.72 +{ 1.73 + *aSearchStatus = mSearchStatus; 1.74 + return NS_OK; 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP 1.78 +nsAutoCompleteController::GetMatchCount(uint32_t *aMatchCount) 1.79 +{ 1.80 + *aMatchCount = mRowCount; 1.81 + return NS_OK; 1.82 +} 1.83 + 1.84 +NS_IMETHODIMP 1.85 +nsAutoCompleteController::GetInput(nsIAutoCompleteInput **aInput) 1.86 +{ 1.87 + *aInput = mInput; 1.88 + NS_IF_ADDREF(*aInput); 1.89 + return NS_OK; 1.90 +} 1.91 + 1.92 +NS_IMETHODIMP 1.93 +nsAutoCompleteController::SetInput(nsIAutoCompleteInput *aInput) 1.94 +{ 1.95 + // Don't do anything if the input isn't changing. 1.96 + if (mInput == aInput) 1.97 + return NS_OK; 1.98 + 1.99 + // Clear out the current search context 1.100 + if (mInput) { 1.101 + // Stop all searches in case they are async. 1.102 + StopSearch(); 1.103 + ClearResults(); 1.104 + ClosePopup(); 1.105 + mSearches.Clear(); 1.106 + } 1.107 + 1.108 + mInput = aInput; 1.109 + 1.110 + // Nothing more to do if the input was just being set to null. 1.111 + if (!aInput) 1.112 + return NS_OK; 1.113 + 1.114 + nsAutoString newValue; 1.115 + aInput->GetTextValue(newValue); 1.116 + 1.117 + // Clear out this reference in case the new input's popup has no tree 1.118 + mTree = nullptr; 1.119 + 1.120 + // Reset all search state members to default values 1.121 + mSearchString = newValue; 1.122 + mDefaultIndexCompleted = false; 1.123 + mBackspaced = false; 1.124 + mSearchStatus = nsIAutoCompleteController::STATUS_NONE; 1.125 + mRowCount = 0; 1.126 + mSearchesOngoing = 0; 1.127 + 1.128 + // Initialize our list of search objects 1.129 + uint32_t searchCount; 1.130 + aInput->GetSearchCount(&searchCount); 1.131 + mResults.SetCapacity(searchCount); 1.132 + mSearches.SetCapacity(searchCount); 1.133 + mMatchCounts.SetLength(searchCount); 1.134 + mImmediateSearchesCount = 0; 1.135 + 1.136 + const char *searchCID = kAutoCompleteSearchCID; 1.137 + 1.138 + for (uint32_t i = 0; i < searchCount; ++i) { 1.139 + // Use the search name to create the contract id string for the search service 1.140 + nsAutoCString searchName; 1.141 + aInput->GetSearchAt(i, searchName); 1.142 + nsAutoCString cid(searchCID); 1.143 + cid.Append(searchName); 1.144 + 1.145 + // Use the created cid to get a pointer to the search service and store it for later 1.146 + nsCOMPtr<nsIAutoCompleteSearch> search = do_GetService(cid.get()); 1.147 + if (search) { 1.148 + mSearches.AppendObject(search); 1.149 + 1.150 + // Count immediate searches. 1.151 + uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED; 1.152 + nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc = 1.153 + do_QueryInterface(search); 1.154 + if (searchDesc && NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) && 1.155 + searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE) 1.156 + mImmediateSearchesCount++; 1.157 + } 1.158 + } 1.159 + 1.160 + return NS_OK; 1.161 +} 1.162 + 1.163 +NS_IMETHODIMP 1.164 +nsAutoCompleteController::StartSearch(const nsAString &aSearchString) 1.165 +{ 1.166 + mSearchString = aSearchString; 1.167 + StartSearches(); 1.168 + return NS_OK; 1.169 +} 1.170 + 1.171 +NS_IMETHODIMP 1.172 +nsAutoCompleteController::HandleText() 1.173 +{ 1.174 + // Note: the events occur in the following order when IME is used. 1.175 + // 1. a compositionstart event(HandleStartComposition) 1.176 + // 2. some input events (HandleText), eCompositionState_Composing 1.177 + // 3. a compositionend event(HandleEndComposition) 1.178 + // 4. an input event(HandleText), eCompositionState_Committing 1.179 + // We should do nothing during composition. 1.180 + if (mCompositionState == eCompositionState_Composing) { 1.181 + return NS_OK; 1.182 + } 1.183 + 1.184 + bool handlingCompositionCommit = 1.185 + (mCompositionState == eCompositionState_Committing); 1.186 + bool popupClosedByCompositionStart = mPopupClosedByCompositionStart; 1.187 + if (handlingCompositionCommit) { 1.188 + mCompositionState = eCompositionState_None; 1.189 + mPopupClosedByCompositionStart = false; 1.190 + } 1.191 + 1.192 + if (!mInput) { 1.193 + // Stop all searches in case they are async. 1.194 + StopSearch(); 1.195 + // Note: if now is after blur and IME end composition, 1.196 + // check mInput before calling. 1.197 + // See https://bugzilla.mozilla.org/show_bug.cgi?id=193544#c31 1.198 + NS_ERROR("Called before attaching to the control or after detaching from the control"); 1.199 + return NS_OK; 1.200 + } 1.201 + 1.202 + nsAutoString newValue; 1.203 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.204 + input->GetTextValue(newValue); 1.205 + 1.206 + // Stop all searches in case they are async. 1.207 + StopSearch(); 1.208 + 1.209 + if (!mInput) { 1.210 + // StopSearch() can call PostSearchCleanup() which might result 1.211 + // in a blur event, which could null out mInput, so we need to check it 1.212 + // again. See bug #395344 for more details 1.213 + return NS_OK; 1.214 + } 1.215 + 1.216 + bool disabled; 1.217 + input->GetDisableAutoComplete(&disabled); 1.218 + NS_ENSURE_TRUE(!disabled, NS_OK); 1.219 + 1.220 + // Don't search again if the new string is the same as the last search 1.221 + // However, if this is called immediately after compositionend event, 1.222 + // we need to search the same value again since the search was canceled 1.223 + // at compositionstart event handler. 1.224 + if (!handlingCompositionCommit && newValue.Length() > 0 && 1.225 + newValue.Equals(mSearchString)) { 1.226 + return NS_OK; 1.227 + } 1.228 + 1.229 + // Determine if the user has removed text from the end (probably by backspacing) 1.230 + if (newValue.Length() < mSearchString.Length() && 1.231 + Substring(mSearchString, 0, newValue.Length()).Equals(newValue)) 1.232 + { 1.233 + // We need to throw away previous results so we don't try to search through them again 1.234 + ClearResults(); 1.235 + mBackspaced = true; 1.236 + } else 1.237 + mBackspaced = false; 1.238 + 1.239 + mSearchString = newValue; 1.240 + 1.241 + // Don't search if the value is empty 1.242 + if (newValue.Length() == 0) { 1.243 + // If autocomplete popup was closed by compositionstart event handler, 1.244 + // we should reopen it forcibly even if the value is empty. 1.245 + if (popupClosedByCompositionStart && handlingCompositionCommit) { 1.246 + bool cancel; 1.247 + HandleKeyNavigation(nsIDOMKeyEvent::DOM_VK_DOWN, &cancel); 1.248 + return NS_OK; 1.249 + } 1.250 + ClosePopup(); 1.251 + return NS_OK; 1.252 + } 1.253 + 1.254 + StartSearches(); 1.255 + 1.256 + return NS_OK; 1.257 +} 1.258 + 1.259 +NS_IMETHODIMP 1.260 +nsAutoCompleteController::HandleEnter(bool aIsPopupSelection, bool *_retval) 1.261 +{ 1.262 + *_retval = false; 1.263 + if (!mInput) 1.264 + return NS_OK; 1.265 + 1.266 + // allow the event through unless there is something selected in the popup 1.267 + mInput->GetPopupOpen(_retval); 1.268 + if (*_retval) { 1.269 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.270 + mInput->GetPopup(getter_AddRefs(popup)); 1.271 + 1.272 + if (popup) { 1.273 + int32_t selectedIndex; 1.274 + popup->GetSelectedIndex(&selectedIndex); 1.275 + *_retval = selectedIndex >= 0; 1.276 + } 1.277 + } 1.278 + 1.279 + // Stop the search, and handle the enter. 1.280 + StopSearch(); 1.281 + EnterMatch(aIsPopupSelection); 1.282 + 1.283 + return NS_OK; 1.284 +} 1.285 + 1.286 +NS_IMETHODIMP 1.287 +nsAutoCompleteController::HandleEscape(bool *_retval) 1.288 +{ 1.289 + *_retval = false; 1.290 + if (!mInput) 1.291 + return NS_OK; 1.292 + 1.293 + // allow the event through if the popup is closed 1.294 + mInput->GetPopupOpen(_retval); 1.295 + 1.296 + // Stop all searches in case they are async. 1.297 + StopSearch(); 1.298 + ClearResults(); 1.299 + RevertTextValue(); 1.300 + ClosePopup(); 1.301 + 1.302 + return NS_OK; 1.303 +} 1.304 + 1.305 +NS_IMETHODIMP 1.306 +nsAutoCompleteController::HandleStartComposition() 1.307 +{ 1.308 + NS_ENSURE_TRUE(mCompositionState != eCompositionState_Composing, NS_OK); 1.309 + 1.310 + mPopupClosedByCompositionStart = false; 1.311 + mCompositionState = eCompositionState_Composing; 1.312 + 1.313 + if (!mInput) 1.314 + return NS_OK; 1.315 + 1.316 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.317 + bool disabled; 1.318 + input->GetDisableAutoComplete(&disabled); 1.319 + if (disabled) 1.320 + return NS_OK; 1.321 + 1.322 + // Stop all searches in case they are async. 1.323 + StopSearch(); 1.324 + 1.325 + bool isOpen = false; 1.326 + input->GetPopupOpen(&isOpen); 1.327 + if (isOpen) { 1.328 + ClosePopup(); 1.329 + 1.330 + bool stillOpen = false; 1.331 + input->GetPopupOpen(&stillOpen); 1.332 + mPopupClosedByCompositionStart = !stillOpen; 1.333 + } 1.334 + return NS_OK; 1.335 +} 1.336 + 1.337 +NS_IMETHODIMP 1.338 +nsAutoCompleteController::HandleEndComposition() 1.339 +{ 1.340 + NS_ENSURE_TRUE(mCompositionState == eCompositionState_Composing, NS_OK); 1.341 + 1.342 + // We can't yet retrieve the committed value from the editor, since it isn't 1.343 + // completely committed yet. Set mCompositionState to 1.344 + // eCompositionState_Committing, so that when HandleText() is called (in 1.345 + // response to the "input" event), we know that we should handle the 1.346 + // committed text. 1.347 + mCompositionState = eCompositionState_Committing; 1.348 + return NS_OK; 1.349 +} 1.350 + 1.351 +NS_IMETHODIMP 1.352 +nsAutoCompleteController::HandleTab() 1.353 +{ 1.354 + bool cancel; 1.355 + return HandleEnter(false, &cancel); 1.356 +} 1.357 + 1.358 +NS_IMETHODIMP 1.359 +nsAutoCompleteController::HandleKeyNavigation(uint32_t aKey, bool *_retval) 1.360 +{ 1.361 + // By default, don't cancel the event 1.362 + *_retval = false; 1.363 + 1.364 + if (!mInput) { 1.365 + // Stop all searches in case they are async. 1.366 + StopSearch(); 1.367 + // Note: if now is after blur and IME end composition, 1.368 + // check mInput before calling. 1.369 + // See https://bugzilla.mozilla.org/show_bug.cgi?id=193544#c31 1.370 + NS_ERROR("Called before attaching to the control or after detaching from the control"); 1.371 + return NS_OK; 1.372 + } 1.373 + 1.374 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.375 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.376 + input->GetPopup(getter_AddRefs(popup)); 1.377 + NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); 1.378 + 1.379 + bool disabled; 1.380 + input->GetDisableAutoComplete(&disabled); 1.381 + NS_ENSURE_TRUE(!disabled, NS_OK); 1.382 + 1.383 + if (aKey == nsIDOMKeyEvent::DOM_VK_UP || 1.384 + aKey == nsIDOMKeyEvent::DOM_VK_DOWN || 1.385 + aKey == nsIDOMKeyEvent::DOM_VK_PAGE_UP || 1.386 + aKey == nsIDOMKeyEvent::DOM_VK_PAGE_DOWN) 1.387 + { 1.388 + // Prevent the input from handling up/down events, as it may move 1.389 + // the cursor to home/end on some systems 1.390 + *_retval = true; 1.391 + 1.392 + bool isOpen = false; 1.393 + input->GetPopupOpen(&isOpen); 1.394 + if (isOpen) { 1.395 + bool reverse = aKey == nsIDOMKeyEvent::DOM_VK_UP || 1.396 + aKey == nsIDOMKeyEvent::DOM_VK_PAGE_UP ? true : false; 1.397 + bool page = aKey == nsIDOMKeyEvent::DOM_VK_PAGE_UP || 1.398 + aKey == nsIDOMKeyEvent::DOM_VK_PAGE_DOWN ? true : false; 1.399 + 1.400 + // Fill in the value of the textbox with whatever is selected in the popup 1.401 + // if the completeSelectedIndex attribute is set. We check this before 1.402 + // calling SelectBy of an earlier attempt to avoid crashing. 1.403 + bool completeSelection; 1.404 + input->GetCompleteSelectedIndex(&completeSelection); 1.405 + 1.406 + // Instruct the result view to scroll by the given amount and direction 1.407 + popup->SelectBy(reverse, page); 1.408 + 1.409 + if (completeSelection) 1.410 + { 1.411 + int32_t selectedIndex; 1.412 + popup->GetSelectedIndex(&selectedIndex); 1.413 + if (selectedIndex >= 0) { 1.414 + // A result is selected, so fill in its value 1.415 + nsAutoString value; 1.416 + if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, false, value))) { 1.417 + input->SetTextValue(value); 1.418 + input->SelectTextRange(value.Length(), value.Length()); 1.419 + } 1.420 + } else { 1.421 + // Nothing is selected, so fill in the last typed value 1.422 + input->SetTextValue(mSearchString); 1.423 + input->SelectTextRange(mSearchString.Length(), mSearchString.Length()); 1.424 + } 1.425 + } 1.426 + } else { 1.427 +#ifdef XP_MACOSX 1.428 + // on Mac, only show the popup if the caret is at the start or end of 1.429 + // the input and there is no selection, so that the default defined key 1.430 + // shortcuts for up and down move to the beginning and end of the field 1.431 + // otherwise. 1.432 + int32_t start, end; 1.433 + if (aKey == nsIDOMKeyEvent::DOM_VK_UP) { 1.434 + input->GetSelectionStart(&start); 1.435 + input->GetSelectionEnd(&end); 1.436 + if (start > 0 || start != end) 1.437 + *_retval = false; 1.438 + } 1.439 + else if (aKey == nsIDOMKeyEvent::DOM_VK_DOWN) { 1.440 + nsAutoString text; 1.441 + input->GetTextValue(text); 1.442 + input->GetSelectionStart(&start); 1.443 + input->GetSelectionEnd(&end); 1.444 + if (start != end || end < (int32_t)text.Length()) 1.445 + *_retval = false; 1.446 + } 1.447 +#endif 1.448 + if (*_retval) { 1.449 + // Open the popup if there has been a previous search, or else kick off a new search 1.450 + if (!mResults.IsEmpty()) { 1.451 + if (mRowCount) { 1.452 + OpenPopup(); 1.453 + } 1.454 + } else { 1.455 + // Stop all searches in case they are async. 1.456 + StopSearch(); 1.457 + 1.458 + if (!mInput) { 1.459 + // StopSearch() can call PostSearchCleanup() which might result 1.460 + // in a blur event, which could null out mInput, so we need to check it 1.461 + // again. See bug #395344 for more details 1.462 + return NS_OK; 1.463 + } 1.464 + 1.465 + StartSearches(); 1.466 + } 1.467 + } 1.468 + } 1.469 + } else if ( aKey == nsIDOMKeyEvent::DOM_VK_LEFT 1.470 + || aKey == nsIDOMKeyEvent::DOM_VK_RIGHT 1.471 +#ifndef XP_MACOSX 1.472 + || aKey == nsIDOMKeyEvent::DOM_VK_HOME 1.473 +#endif 1.474 + ) 1.475 + { 1.476 + // The user hit a text-navigation key. 1.477 + bool isOpen = false; 1.478 + input->GetPopupOpen(&isOpen); 1.479 + if (isOpen) { 1.480 + int32_t selectedIndex; 1.481 + popup->GetSelectedIndex(&selectedIndex); 1.482 + bool shouldComplete; 1.483 + input->GetCompleteDefaultIndex(&shouldComplete); 1.484 + if (selectedIndex >= 0) { 1.485 + // The pop-up is open and has a selection, take its value 1.486 + nsAutoString value; 1.487 + if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, false, value))) { 1.488 + input->SetTextValue(value); 1.489 + input->SelectTextRange(value.Length(), value.Length()); 1.490 + } 1.491 + } 1.492 + else if (shouldComplete) { 1.493 + // We usually try to preserve the casing of what user has typed, but 1.494 + // if he wants to autocomplete, we will replace the value with the 1.495 + // actual autocomplete result. 1.496 + // The user wants explicitely to use that result, so this ensures 1.497 + // association of the result with the autocompleted text. 1.498 + nsAutoString value; 1.499 + nsAutoString inputValue; 1.500 + input->GetTextValue(inputValue); 1.501 + if (NS_SUCCEEDED(GetDefaultCompleteValue(-1, false, value)) && 1.502 + value.Equals(inputValue, nsCaseInsensitiveStringComparator())) { 1.503 + input->SetTextValue(value); 1.504 + input->SelectTextRange(value.Length(), value.Length()); 1.505 + } 1.506 + } 1.507 + // Close the pop-up even if nothing was selected 1.508 + ClearSearchTimer(); 1.509 + ClosePopup(); 1.510 + } 1.511 + // Update last-searched string to the current input, since the input may 1.512 + // have changed. Without this, subsequent backspaces look like text 1.513 + // additions, not text deletions. 1.514 + nsAutoString value; 1.515 + input->GetTextValue(value); 1.516 + mSearchString = value; 1.517 + } 1.518 + 1.519 + return NS_OK; 1.520 +} 1.521 + 1.522 +NS_IMETHODIMP 1.523 +nsAutoCompleteController::HandleDelete(bool *_retval) 1.524 +{ 1.525 + *_retval = false; 1.526 + if (!mInput) 1.527 + return NS_OK; 1.528 + 1.529 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.530 + bool isOpen = false; 1.531 + input->GetPopupOpen(&isOpen); 1.532 + if (!isOpen || mRowCount <= 0) { 1.533 + // Nothing left to delete, proceed as normal 1.534 + HandleText(); 1.535 + return NS_OK; 1.536 + } 1.537 + 1.538 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.539 + input->GetPopup(getter_AddRefs(popup)); 1.540 + 1.541 + int32_t index, searchIndex, rowIndex; 1.542 + popup->GetSelectedIndex(&index); 1.543 + if (index == -1) { 1.544 + // No row is selected in the list 1.545 + HandleText(); 1.546 + return NS_OK; 1.547 + } 1.548 + 1.549 + RowIndexToSearch(index, &searchIndex, &rowIndex); 1.550 + NS_ENSURE_TRUE(searchIndex >= 0 && rowIndex >= 0, NS_ERROR_FAILURE); 1.551 + 1.552 + nsIAutoCompleteResult *result = mResults[searchIndex]; 1.553 + NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); 1.554 + 1.555 + nsAutoString search; 1.556 + input->GetSearchParam(search); 1.557 + 1.558 + // Clear the row in our result and in the DB. 1.559 + result->RemoveValueAt(rowIndex, true); 1.560 + --mRowCount; 1.561 + 1.562 + // We removed it, so make sure we cancel the event that triggered this call. 1.563 + *_retval = true; 1.564 + 1.565 + // Unselect the current item. 1.566 + popup->SetSelectedIndex(-1); 1.567 + 1.568 + // Tell the tree that the row count changed. 1.569 + if (mTree) 1.570 + mTree->RowCountChanged(mRowCount, -1); 1.571 + 1.572 + // Adjust index, if needed. 1.573 + if (index >= (int32_t)mRowCount) 1.574 + index = mRowCount - 1; 1.575 + 1.576 + if (mRowCount > 0) { 1.577 + // There are still rows in the popup, select the current index again. 1.578 + popup->SetSelectedIndex(index); 1.579 + 1.580 + // Complete to the new current value. 1.581 + bool shouldComplete = false; 1.582 + input->GetCompleteDefaultIndex(&shouldComplete); 1.583 + if (shouldComplete) { 1.584 + nsAutoString value; 1.585 + if (NS_SUCCEEDED(GetResultValueAt(index, false, value))) { 1.586 + CompleteValue(value); 1.587 + } 1.588 + } 1.589 + 1.590 + // Invalidate the popup. 1.591 + popup->Invalidate(); 1.592 + } else { 1.593 + // Nothing left in the popup, clear any pending search timers and 1.594 + // close the popup. 1.595 + ClearSearchTimer(); 1.596 + uint32_t minResults; 1.597 + input->GetMinResultsForPopup(&minResults); 1.598 + if (minResults) { 1.599 + ClosePopup(); 1.600 + } 1.601 + } 1.602 + 1.603 + return NS_OK; 1.604 +} 1.605 + 1.606 +nsresult 1.607 +nsAutoCompleteController::GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aResult, 1.608 + int32_t* aRowIndex) 1.609 +{ 1.610 + int32_t searchIndex; 1.611 + RowIndexToSearch(aIndex, &searchIndex, aRowIndex); 1.612 + NS_ENSURE_TRUE(searchIndex >= 0 && *aRowIndex >= 0, NS_ERROR_FAILURE); 1.613 + 1.614 + *aResult = mResults[searchIndex]; 1.615 + NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE); 1.616 + return NS_OK; 1.617 +} 1.618 + 1.619 +NS_IMETHODIMP 1.620 +nsAutoCompleteController::GetValueAt(int32_t aIndex, nsAString & _retval) 1.621 +{ 1.622 + GetResultLabelAt(aIndex, _retval); 1.623 + 1.624 + return NS_OK; 1.625 +} 1.626 + 1.627 +NS_IMETHODIMP 1.628 +nsAutoCompleteController::GetLabelAt(int32_t aIndex, nsAString & _retval) 1.629 +{ 1.630 + GetResultLabelAt(aIndex, _retval); 1.631 + 1.632 + return NS_OK; 1.633 +} 1.634 + 1.635 +NS_IMETHODIMP 1.636 +nsAutoCompleteController::GetCommentAt(int32_t aIndex, nsAString & _retval) 1.637 +{ 1.638 + int32_t rowIndex; 1.639 + nsIAutoCompleteResult* result; 1.640 + nsresult rv = GetResultAt(aIndex, &result, &rowIndex); 1.641 + NS_ENSURE_SUCCESS(rv, rv); 1.642 + 1.643 + return result->GetCommentAt(rowIndex, _retval); 1.644 +} 1.645 + 1.646 +NS_IMETHODIMP 1.647 +nsAutoCompleteController::GetStyleAt(int32_t aIndex, nsAString & _retval) 1.648 +{ 1.649 + int32_t rowIndex; 1.650 + nsIAutoCompleteResult* result; 1.651 + nsresult rv = GetResultAt(aIndex, &result, &rowIndex); 1.652 + NS_ENSURE_SUCCESS(rv, rv); 1.653 + 1.654 + return result->GetStyleAt(rowIndex, _retval); 1.655 +} 1.656 + 1.657 +NS_IMETHODIMP 1.658 +nsAutoCompleteController::GetImageAt(int32_t aIndex, nsAString & _retval) 1.659 +{ 1.660 + int32_t rowIndex; 1.661 + nsIAutoCompleteResult* result; 1.662 + nsresult rv = GetResultAt(aIndex, &result, &rowIndex); 1.663 + NS_ENSURE_SUCCESS(rv, rv); 1.664 + 1.665 + return result->GetImageAt(rowIndex, _retval); 1.666 +} 1.667 + 1.668 +NS_IMETHODIMP 1.669 +nsAutoCompleteController::GetFinalCompleteValueAt(int32_t aIndex, 1.670 + nsAString & _retval) 1.671 +{ 1.672 + int32_t rowIndex; 1.673 + nsIAutoCompleteResult* result; 1.674 + nsresult rv = GetResultAt(aIndex, &result, &rowIndex); 1.675 + NS_ENSURE_SUCCESS(rv, rv); 1.676 + 1.677 + return result->GetFinalCompleteValueAt(rowIndex, _retval); 1.678 +} 1.679 + 1.680 +NS_IMETHODIMP 1.681 +nsAutoCompleteController::SetSearchString(const nsAString &aSearchString) 1.682 +{ 1.683 + mSearchString = aSearchString; 1.684 + return NS_OK; 1.685 +} 1.686 + 1.687 +NS_IMETHODIMP 1.688 +nsAutoCompleteController::GetSearchString(nsAString &aSearchString) 1.689 +{ 1.690 + aSearchString = mSearchString; 1.691 + return NS_OK; 1.692 +} 1.693 + 1.694 + 1.695 +//////////////////////////////////////////////////////////////////////// 1.696 +//// nsIAutoCompleteObserver 1.697 + 1.698 +NS_IMETHODIMP 1.699 +nsAutoCompleteController::OnUpdateSearchResult(nsIAutoCompleteSearch *aSearch, nsIAutoCompleteResult* aResult) 1.700 +{ 1.701 + ClearResults(); 1.702 + return OnSearchResult(aSearch, aResult); 1.703 +} 1.704 + 1.705 +NS_IMETHODIMP 1.706 +nsAutoCompleteController::OnSearchResult(nsIAutoCompleteSearch *aSearch, nsIAutoCompleteResult* aResult) 1.707 +{ 1.708 + // look up the index of the search which is returning 1.709 + for (uint32_t i = 0; i < mSearches.Length(); ++i) { 1.710 + if (mSearches[i] == aSearch) { 1.711 + ProcessResult(i, aResult); 1.712 + } 1.713 + } 1.714 + 1.715 + return NS_OK; 1.716 +} 1.717 + 1.718 +//////////////////////////////////////////////////////////////////////// 1.719 +//// nsITimerCallback 1.720 + 1.721 +NS_IMETHODIMP 1.722 +nsAutoCompleteController::Notify(nsITimer *timer) 1.723 +{ 1.724 + mTimer = nullptr; 1.725 + 1.726 + if (mImmediateSearchesCount == 0) { 1.727 + // If there were no immediate searches, BeforeSearches has not yet been 1.728 + // called, so do it now. 1.729 + nsresult rv = BeforeSearches(); 1.730 + if (NS_FAILED(rv)) 1.731 + return rv; 1.732 + } 1.733 + StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED); 1.734 + AfterSearches(); 1.735 + return NS_OK; 1.736 +} 1.737 + 1.738 +//////////////////////////////////////////////////////////////////////// 1.739 +// nsITreeView 1.740 + 1.741 +NS_IMETHODIMP 1.742 +nsAutoCompleteController::GetRowCount(int32_t *aRowCount) 1.743 +{ 1.744 + *aRowCount = mRowCount; 1.745 + return NS_OK; 1.746 +} 1.747 + 1.748 +NS_IMETHODIMP 1.749 +nsAutoCompleteController::GetRowProperties(int32_t index, nsAString& aProps) 1.750 +{ 1.751 + return NS_OK; 1.752 +} 1.753 + 1.754 +NS_IMETHODIMP 1.755 +nsAutoCompleteController::GetCellProperties(int32_t row, nsITreeColumn* col, 1.756 + nsAString& aProps) 1.757 +{ 1.758 + if (row >= 0) { 1.759 + GetStyleAt(row, aProps); 1.760 + } 1.761 + 1.762 + return NS_OK; 1.763 +} 1.764 + 1.765 +NS_IMETHODIMP 1.766 +nsAutoCompleteController::GetColumnProperties(nsITreeColumn* col, nsAString& aProps) 1.767 +{ 1.768 + return NS_OK; 1.769 +} 1.770 + 1.771 +NS_IMETHODIMP 1.772 +nsAutoCompleteController::GetImageSrc(int32_t row, nsITreeColumn* col, nsAString& _retval) 1.773 +{ 1.774 + const char16_t* colID; 1.775 + col->GetIdConst(&colID); 1.776 + 1.777 + if (NS_LITERAL_STRING("treecolAutoCompleteValue").Equals(colID)) 1.778 + return GetImageAt(row, _retval); 1.779 + 1.780 + return NS_OK; 1.781 +} 1.782 + 1.783 +NS_IMETHODIMP 1.784 +nsAutoCompleteController::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval) 1.785 +{ 1.786 + NS_NOTREACHED("tree has no progress cells"); 1.787 + return NS_OK; 1.788 +} 1.789 + 1.790 +NS_IMETHODIMP 1.791 +nsAutoCompleteController::GetCellValue(int32_t row, nsITreeColumn* col, nsAString& _retval) 1.792 +{ 1.793 + NS_NOTREACHED("all of our cells are text"); 1.794 + return NS_OK; 1.795 +} 1.796 + 1.797 +NS_IMETHODIMP 1.798 +nsAutoCompleteController::GetCellText(int32_t row, nsITreeColumn* col, nsAString& _retval) 1.799 +{ 1.800 + const char16_t* colID; 1.801 + col->GetIdConst(&colID); 1.802 + 1.803 + if (NS_LITERAL_STRING("treecolAutoCompleteValue").Equals(colID)) 1.804 + GetValueAt(row, _retval); 1.805 + else if (NS_LITERAL_STRING("treecolAutoCompleteComment").Equals(colID)) 1.806 + GetCommentAt(row, _retval); 1.807 + 1.808 + return NS_OK; 1.809 +} 1.810 + 1.811 +NS_IMETHODIMP 1.812 +nsAutoCompleteController::IsContainer(int32_t index, bool *_retval) 1.813 +{ 1.814 + *_retval = false; 1.815 + return NS_OK; 1.816 +} 1.817 + 1.818 +NS_IMETHODIMP 1.819 +nsAutoCompleteController::IsContainerOpen(int32_t index, bool *_retval) 1.820 +{ 1.821 + NS_NOTREACHED("no container cells"); 1.822 + return NS_OK; 1.823 +} 1.824 + 1.825 +NS_IMETHODIMP 1.826 +nsAutoCompleteController::IsContainerEmpty(int32_t index, bool *_retval) 1.827 +{ 1.828 + NS_NOTREACHED("no container cells"); 1.829 + return NS_OK; 1.830 +} 1.831 + 1.832 +NS_IMETHODIMP 1.833 +nsAutoCompleteController::GetLevel(int32_t index, int32_t *_retval) 1.834 +{ 1.835 + *_retval = 0; 1.836 + return NS_OK; 1.837 +} 1.838 + 1.839 +NS_IMETHODIMP 1.840 +nsAutoCompleteController::GetParentIndex(int32_t rowIndex, int32_t *_retval) 1.841 +{ 1.842 + *_retval = -1; 1.843 + return NS_OK; 1.844 +} 1.845 + 1.846 +NS_IMETHODIMP 1.847 +nsAutoCompleteController::HasNextSibling(int32_t rowIndex, int32_t afterIndex, bool *_retval) 1.848 +{ 1.849 + *_retval = false; 1.850 + return NS_OK; 1.851 +} 1.852 + 1.853 +NS_IMETHODIMP 1.854 +nsAutoCompleteController::ToggleOpenState(int32_t index) 1.855 +{ 1.856 + return NS_OK; 1.857 +} 1.858 + 1.859 +NS_IMETHODIMP 1.860 +nsAutoCompleteController::SetTree(nsITreeBoxObject *tree) 1.861 +{ 1.862 + mTree = tree; 1.863 + return NS_OK; 1.864 +} 1.865 + 1.866 +NS_IMETHODIMP 1.867 +nsAutoCompleteController::GetSelection(nsITreeSelection * *aSelection) 1.868 +{ 1.869 + *aSelection = mSelection; 1.870 + NS_IF_ADDREF(*aSelection); 1.871 + return NS_OK; 1.872 +} 1.873 + 1.874 +NS_IMETHODIMP nsAutoCompleteController::SetSelection(nsITreeSelection * aSelection) 1.875 +{ 1.876 + mSelection = aSelection; 1.877 + return NS_OK; 1.878 +} 1.879 + 1.880 +NS_IMETHODIMP 1.881 +nsAutoCompleteController::SelectionChanged() 1.882 +{ 1.883 + return NS_OK; 1.884 +} 1.885 + 1.886 +NS_IMETHODIMP 1.887 +nsAutoCompleteController::SetCellValue(int32_t row, nsITreeColumn* col, const nsAString& value) 1.888 +{ 1.889 + return NS_OK; 1.890 +} 1.891 + 1.892 +NS_IMETHODIMP 1.893 +nsAutoCompleteController::SetCellText(int32_t row, nsITreeColumn* col, const nsAString& value) 1.894 +{ 1.895 + return NS_OK; 1.896 +} 1.897 + 1.898 +NS_IMETHODIMP 1.899 +nsAutoCompleteController::CycleHeader(nsITreeColumn* col) 1.900 +{ 1.901 + return NS_OK; 1.902 +} 1.903 + 1.904 +NS_IMETHODIMP 1.905 +nsAutoCompleteController::CycleCell(int32_t row, nsITreeColumn* col) 1.906 +{ 1.907 + return NS_OK; 1.908 +} 1.909 + 1.910 +NS_IMETHODIMP 1.911 +nsAutoCompleteController::IsEditable(int32_t row, nsITreeColumn* col, bool *_retval) 1.912 +{ 1.913 + *_retval = false; 1.914 + return NS_OK; 1.915 +} 1.916 + 1.917 +NS_IMETHODIMP 1.918 +nsAutoCompleteController::IsSelectable(int32_t row, nsITreeColumn* col, bool *_retval) 1.919 +{ 1.920 + *_retval = false; 1.921 + return NS_OK; 1.922 +} 1.923 + 1.924 +NS_IMETHODIMP 1.925 +nsAutoCompleteController::IsSeparator(int32_t index, bool *_retval) 1.926 +{ 1.927 + *_retval = false; 1.928 + return NS_OK; 1.929 +} 1.930 + 1.931 +NS_IMETHODIMP 1.932 +nsAutoCompleteController::IsSorted(bool *_retval) 1.933 +{ 1.934 + *_retval = false; 1.935 + return NS_OK; 1.936 +} 1.937 + 1.938 +NS_IMETHODIMP 1.939 +nsAutoCompleteController::CanDrop(int32_t index, int32_t orientation, 1.940 + nsIDOMDataTransfer* dataTransfer, bool *_retval) 1.941 +{ 1.942 + return NS_OK; 1.943 +} 1.944 + 1.945 +NS_IMETHODIMP 1.946 +nsAutoCompleteController::Drop(int32_t row, int32_t orientation, nsIDOMDataTransfer* dataTransfer) 1.947 +{ 1.948 + return NS_OK; 1.949 +} 1.950 + 1.951 +NS_IMETHODIMP 1.952 +nsAutoCompleteController::PerformAction(const char16_t *action) 1.953 +{ 1.954 + return NS_OK; 1.955 +} 1.956 + 1.957 +NS_IMETHODIMP 1.958 +nsAutoCompleteController::PerformActionOnRow(const char16_t *action, int32_t row) 1.959 +{ 1.960 + return NS_OK; 1.961 +} 1.962 + 1.963 +NS_IMETHODIMP 1.964 +nsAutoCompleteController::PerformActionOnCell(const char16_t* action, int32_t row, nsITreeColumn* col) 1.965 +{ 1.966 + return NS_OK; 1.967 +} 1.968 + 1.969 +//////////////////////////////////////////////////////////////////////// 1.970 +//// nsAutoCompleteController 1.971 + 1.972 +nsresult 1.973 +nsAutoCompleteController::OpenPopup() 1.974 +{ 1.975 + uint32_t minResults; 1.976 + mInput->GetMinResultsForPopup(&minResults); 1.977 + 1.978 + if (mRowCount >= minResults) { 1.979 + return mInput->SetPopupOpen(true); 1.980 + } 1.981 + 1.982 + return NS_OK; 1.983 +} 1.984 + 1.985 +nsresult 1.986 +nsAutoCompleteController::ClosePopup() 1.987 +{ 1.988 + if (!mInput) { 1.989 + return NS_OK; 1.990 + } 1.991 + 1.992 + bool isOpen = false; 1.993 + mInput->GetPopupOpen(&isOpen); 1.994 + if (!isOpen) 1.995 + return NS_OK; 1.996 + 1.997 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.998 + mInput->GetPopup(getter_AddRefs(popup)); 1.999 + NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); 1.1000 + popup->SetSelectedIndex(-1); 1.1001 + return mInput->SetPopupOpen(false); 1.1002 +} 1.1003 + 1.1004 +nsresult 1.1005 +nsAutoCompleteController::BeforeSearches() 1.1006 +{ 1.1007 + NS_ENSURE_STATE(mInput); 1.1008 + 1.1009 + mSearchStatus = nsIAutoCompleteController::STATUS_SEARCHING; 1.1010 + mDefaultIndexCompleted = false; 1.1011 + 1.1012 + // The first search result will clear mResults array, though we should pass 1.1013 + // the previous result to each search to allow them to reuse it. So we 1.1014 + // temporarily cache current results till AfterSearches(). 1.1015 + if (!mResultCache.AppendObjects(mResults)) { 1.1016 + return NS_ERROR_OUT_OF_MEMORY; 1.1017 + } 1.1018 + 1.1019 + mSearchesOngoing = mSearches.Length(); 1.1020 + mSearchesFailed = 0; 1.1021 + mFirstSearchResult = true; 1.1022 + 1.1023 + // notify the input that the search is beginning 1.1024 + mInput->OnSearchBegin(); 1.1025 + 1.1026 + return NS_OK; 1.1027 +} 1.1028 + 1.1029 +nsresult 1.1030 +nsAutoCompleteController::StartSearch(uint16_t aSearchType) 1.1031 +{ 1.1032 + NS_ENSURE_STATE(mInput); 1.1033 + nsCOMPtr<nsIAutoCompleteInput> input = mInput; 1.1034 + 1.1035 + for (uint32_t i = 0; i < mSearches.Length(); ++i) { 1.1036 + nsCOMPtr<nsIAutoCompleteSearch> search = mSearches[i]; 1.1037 + 1.1038 + // Filter on search type. Not all the searches implement this interface, 1.1039 + // in such a case just consider them delayed. 1.1040 + uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED; 1.1041 + nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc = 1.1042 + do_QueryInterface(search); 1.1043 + if (searchDesc) 1.1044 + searchDesc->GetSearchType(&searchType); 1.1045 + if (searchType != aSearchType) 1.1046 + continue; 1.1047 + 1.1048 + nsIAutoCompleteResult *result = mResultCache.SafeObjectAt(i); 1.1049 + 1.1050 + if (result) { 1.1051 + uint16_t searchResult; 1.1052 + result->GetSearchResult(&searchResult); 1.1053 + if (searchResult != nsIAutoCompleteResult::RESULT_SUCCESS && 1.1054 + searchResult != nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING && 1.1055 + searchResult != nsIAutoCompleteResult::RESULT_NOMATCH) 1.1056 + result = nullptr; 1.1057 + } 1.1058 + 1.1059 + nsAutoString searchParam; 1.1060 + nsresult rv = input->GetSearchParam(searchParam); 1.1061 + if (NS_FAILED(rv)) 1.1062 + return rv; 1.1063 + 1.1064 + rv = search->StartSearch(mSearchString, searchParam, result, static_cast<nsIAutoCompleteObserver *>(this)); 1.1065 + if (NS_FAILED(rv)) { 1.1066 + ++mSearchesFailed; 1.1067 + --mSearchesOngoing; 1.1068 + } 1.1069 + // Because of the joy of nested event loops (which can easily happen when some 1.1070 + // code uses a generator for an asynchronous AutoComplete search), 1.1071 + // nsIAutoCompleteSearch::StartSearch might cause us to be detached from our input 1.1072 + // field. The next time we iterate, we'd be touching something that we shouldn't 1.1073 + // be, and result in a crash. 1.1074 + if (!mInput) { 1.1075 + // The search operation has been finished. 1.1076 + return NS_OK; 1.1077 + } 1.1078 + } 1.1079 + 1.1080 + return NS_OK; 1.1081 +} 1.1082 + 1.1083 +void 1.1084 +nsAutoCompleteController::AfterSearches() 1.1085 +{ 1.1086 + mResultCache.Clear(); 1.1087 + if (mSearchesFailed == mSearches.Length()) 1.1088 + PostSearchCleanup(); 1.1089 +} 1.1090 + 1.1091 +NS_IMETHODIMP 1.1092 +nsAutoCompleteController::StopSearch() 1.1093 +{ 1.1094 + // Stop the timer if there is one 1.1095 + ClearSearchTimer(); 1.1096 + 1.1097 + // Stop any ongoing asynchronous searches 1.1098 + if (mSearchStatus == nsIAutoCompleteController::STATUS_SEARCHING) { 1.1099 + for (uint32_t i = 0; i < mSearches.Length(); ++i) { 1.1100 + nsCOMPtr<nsIAutoCompleteSearch> search = mSearches[i]; 1.1101 + search->StopSearch(); 1.1102 + } 1.1103 + mSearchesOngoing = 0; 1.1104 + // since we were searching, but now we've stopped, 1.1105 + // we need to call PostSearchCleanup() 1.1106 + PostSearchCleanup(); 1.1107 + } 1.1108 + return NS_OK; 1.1109 +} 1.1110 + 1.1111 +nsresult 1.1112 +nsAutoCompleteController::StartSearches() 1.1113 +{ 1.1114 + // Don't create a new search timer if we're already waiting for one to fire. 1.1115 + // If we don't check for this, we won't be able to cancel the original timer 1.1116 + // and may crash when it fires (bug 236659). 1.1117 + if (mTimer || !mInput) 1.1118 + return NS_OK; 1.1119 + 1.1120 + // Get the timeout for delayed searches. 1.1121 + uint32_t timeout; 1.1122 + mInput->GetTimeout(&timeout); 1.1123 + 1.1124 + uint32_t immediateSearchesCount = mImmediateSearchesCount; 1.1125 + if (timeout == 0) { 1.1126 + // All the searches should be executed immediately. 1.1127 + immediateSearchesCount = mSearches.Length(); 1.1128 + } 1.1129 + 1.1130 + if (immediateSearchesCount > 0) { 1.1131 + nsresult rv = BeforeSearches(); 1.1132 + if (NS_FAILED(rv)) 1.1133 + return rv; 1.1134 + StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE); 1.1135 + 1.1136 + if (mSearches.Length() == immediateSearchesCount) { 1.1137 + // Either all searches are immediate, or the timeout is 0. In the 1.1138 + // latter case we still have to execute the delayed searches, otherwise 1.1139 + // this will be a no-op. 1.1140 + StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED); 1.1141 + 1.1142 + // All the searches have been started, just finish. 1.1143 + AfterSearches(); 1.1144 + return NS_OK; 1.1145 + } 1.1146 + } 1.1147 + 1.1148 + MOZ_ASSERT(timeout > 0, "Trying to delay searches with a 0 timeout!"); 1.1149 + 1.1150 + // Now start the delayed searches. 1.1151 + nsresult rv; 1.1152 + mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); 1.1153 + if (NS_FAILED(rv)) 1.1154 + return rv; 1.1155 + rv = mTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT); 1.1156 + if (NS_FAILED(rv)) 1.1157 + mTimer = nullptr; 1.1158 + 1.1159 + return rv; 1.1160 +} 1.1161 + 1.1162 +nsresult 1.1163 +nsAutoCompleteController::ClearSearchTimer() 1.1164 +{ 1.1165 + if (mTimer) { 1.1166 + mTimer->Cancel(); 1.1167 + mTimer = nullptr; 1.1168 + } 1.1169 + return NS_OK; 1.1170 +} 1.1171 + 1.1172 +nsresult 1.1173 +nsAutoCompleteController::EnterMatch(bool aIsPopupSelection) 1.1174 +{ 1.1175 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.1176 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.1177 + input->GetPopup(getter_AddRefs(popup)); 1.1178 + NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); 1.1179 + 1.1180 + bool forceComplete; 1.1181 + input->GetForceComplete(&forceComplete); 1.1182 + 1.1183 + // Ask the popup if it wants to enter a special value into the textbox 1.1184 + nsAutoString value; 1.1185 + popup->GetOverrideValue(value); 1.1186 + if (value.IsEmpty()) { 1.1187 + bool shouldComplete; 1.1188 + input->GetCompleteDefaultIndex(&shouldComplete); 1.1189 + bool completeSelection; 1.1190 + input->GetCompleteSelectedIndex(&completeSelection); 1.1191 + 1.1192 + int32_t selectedIndex; 1.1193 + popup->GetSelectedIndex(&selectedIndex); 1.1194 + if (selectedIndex >= 0) { 1.1195 + // If completeselectedindex is false or a row was selected from the popup, 1.1196 + // enter it into the textbox. If completeselectedindex is true, or 1.1197 + // EnterMatch was called via other means, for instance pressing Enter, 1.1198 + // don't fill in the value as it will have already been filled in as 1.1199 + // needed, unless the final complete value differs. 1.1200 + nsAutoString finalValue, inputValue; 1.1201 + GetResultValueAt(selectedIndex, true, finalValue); 1.1202 + input->GetTextValue(inputValue); 1.1203 + if (!completeSelection || aIsPopupSelection || 1.1204 + !finalValue.Equals(inputValue)) { 1.1205 + value = finalValue; 1.1206 + } 1.1207 + } 1.1208 + else if (shouldComplete) { 1.1209 + // We usually try to preserve the casing of what user has typed, but 1.1210 + // if he wants to autocomplete, we will replace the value with the 1.1211 + // actual autocomplete result. 1.1212 + // The user wants explicitely to use that result, so this ensures 1.1213 + // association of the result with the autocompleted text. 1.1214 + nsAutoString defaultIndexValue; 1.1215 + if (NS_SUCCEEDED(GetFinalDefaultCompleteValue(defaultIndexValue))) 1.1216 + value = defaultIndexValue; 1.1217 + } 1.1218 + 1.1219 + if (forceComplete && value.IsEmpty()) { 1.1220 + // Since nothing was selected, and forceComplete is specified, that means 1.1221 + // we have to find the first default match and enter it instead 1.1222 + for (uint32_t i = 0; i < mResults.Length(); ++i) { 1.1223 + nsIAutoCompleteResult *result = mResults[i]; 1.1224 + 1.1225 + if (result) { 1.1226 + int32_t defaultIndex; 1.1227 + result->GetDefaultIndex(&defaultIndex); 1.1228 + if (defaultIndex >= 0) { 1.1229 + result->GetFinalCompleteValueAt(defaultIndex, value); 1.1230 + break; 1.1231 + } 1.1232 + } 1.1233 + } 1.1234 + } 1.1235 + } 1.1236 + 1.1237 + nsCOMPtr<nsIObserverService> obsSvc = 1.1238 + mozilla::services::GetObserverService(); 1.1239 + NS_ENSURE_STATE(obsSvc); 1.1240 + obsSvc->NotifyObservers(input, "autocomplete-will-enter-text", nullptr); 1.1241 + 1.1242 + if (!value.IsEmpty()) { 1.1243 + input->SetTextValue(value); 1.1244 + input->SelectTextRange(value.Length(), value.Length()); 1.1245 + mSearchString = value; 1.1246 + } 1.1247 + 1.1248 + obsSvc->NotifyObservers(input, "autocomplete-did-enter-text", nullptr); 1.1249 + ClosePopup(); 1.1250 + 1.1251 + bool cancel; 1.1252 + input->OnTextEntered(&cancel); 1.1253 + 1.1254 + return NS_OK; 1.1255 +} 1.1256 + 1.1257 +nsresult 1.1258 +nsAutoCompleteController::RevertTextValue() 1.1259 +{ 1.1260 + // StopSearch() can call PostSearchCleanup() which might result 1.1261 + // in a blur event, which could null out mInput, so we need to check it 1.1262 + // again. See bug #408463 for more details 1.1263 + if (!mInput) 1.1264 + return NS_OK; 1.1265 + 1.1266 + nsAutoString oldValue(mSearchString); 1.1267 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.1268 + 1.1269 + bool cancel = false; 1.1270 + input->OnTextReverted(&cancel); 1.1271 + 1.1272 + if (!cancel) { 1.1273 + nsCOMPtr<nsIObserverService> obsSvc = 1.1274 + mozilla::services::GetObserverService(); 1.1275 + NS_ENSURE_STATE(obsSvc); 1.1276 + obsSvc->NotifyObservers(input, "autocomplete-will-revert-text", nullptr); 1.1277 + 1.1278 + nsAutoString inputValue; 1.1279 + input->GetTextValue(inputValue); 1.1280 + // Don't change the value if it is the same to prevent sending useless events. 1.1281 + // NOTE: how can |RevertTextValue| be called with inputValue != oldValue? 1.1282 + if (!oldValue.Equals(inputValue)) { 1.1283 + input->SetTextValue(oldValue); 1.1284 + } 1.1285 + 1.1286 + obsSvc->NotifyObservers(input, "autocomplete-did-revert-text", nullptr); 1.1287 + } 1.1288 + 1.1289 + return NS_OK; 1.1290 +} 1.1291 + 1.1292 +nsresult 1.1293 +nsAutoCompleteController::ProcessResult(int32_t aSearchIndex, nsIAutoCompleteResult *aResult) 1.1294 +{ 1.1295 + NS_ENSURE_STATE(mInput); 1.1296 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.1297 + 1.1298 + // If this is the first search result we are processing 1.1299 + // we should clear out the previously cached results 1.1300 + if (mFirstSearchResult) { 1.1301 + ClearResults(); 1.1302 + mFirstSearchResult = false; 1.1303 + } 1.1304 + 1.1305 + uint16_t result = 0; 1.1306 + if (aResult) 1.1307 + aResult->GetSearchResult(&result); 1.1308 + 1.1309 + // if our results are incremental, the search is still ongoing 1.1310 + if (result != nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING && 1.1311 + result != nsIAutoCompleteResult::RESULT_NOMATCH_ONGOING) { 1.1312 + --mSearchesOngoing; 1.1313 + } 1.1314 + 1.1315 + uint32_t oldMatchCount = 0; 1.1316 + uint32_t matchCount = 0; 1.1317 + if (aResult) 1.1318 + aResult->GetMatchCount(&matchCount); 1.1319 + 1.1320 + int32_t resultIndex = mResults.IndexOf(aResult); 1.1321 + if (resultIndex == -1) { 1.1322 + // cache the result 1.1323 + mResults.AppendObject(aResult); 1.1324 + mMatchCounts.AppendElement(matchCount); 1.1325 + resultIndex = mResults.Count() - 1; 1.1326 + } 1.1327 + else { 1.1328 + oldMatchCount = mMatchCounts[aSearchIndex]; 1.1329 + mMatchCounts[resultIndex] = matchCount; 1.1330 + } 1.1331 + 1.1332 + bool isTypeAheadResult = false; 1.1333 + if (aResult) { 1.1334 + aResult->GetTypeAheadResult(&isTypeAheadResult); 1.1335 + } 1.1336 + 1.1337 + if (!isTypeAheadResult) { 1.1338 + uint32_t oldRowCount = mRowCount; 1.1339 + // If the search failed, increase the match count to include the error 1.1340 + // description. 1.1341 + if (result == nsIAutoCompleteResult::RESULT_FAILURE) { 1.1342 + nsAutoString error; 1.1343 + aResult->GetErrorDescription(error); 1.1344 + if (!error.IsEmpty()) { 1.1345 + ++mRowCount; 1.1346 + if (mTree) { 1.1347 + mTree->RowCountChanged(oldRowCount, 1); 1.1348 + } 1.1349 + } 1.1350 + } else if (result == nsIAutoCompleteResult::RESULT_SUCCESS || 1.1351 + result == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { 1.1352 + // Increase the match count for all matches in this result. 1.1353 + mRowCount += matchCount - oldMatchCount; 1.1354 + 1.1355 + if (mTree) { 1.1356 + mTree->RowCountChanged(oldRowCount, matchCount - oldMatchCount); 1.1357 + } 1.1358 + } 1.1359 + 1.1360 + // Refresh the popup view to display the new search results 1.1361 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.1362 + input->GetPopup(getter_AddRefs(popup)); 1.1363 + NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); 1.1364 + popup->Invalidate(); 1.1365 + 1.1366 + uint32_t minResults; 1.1367 + input->GetMinResultsForPopup(&minResults); 1.1368 + 1.1369 + // Make sure the popup is open, if necessary, since we now have at least one 1.1370 + // search result ready to display. Don't force the popup closed if we might 1.1371 + // get results in the future to avoid unnecessarily canceling searches. 1.1372 + if (mRowCount || !minResults) { 1.1373 + OpenPopup(); 1.1374 + } else if (result != nsIAutoCompleteResult::RESULT_NOMATCH_ONGOING) { 1.1375 + ClosePopup(); 1.1376 + } 1.1377 + } 1.1378 + 1.1379 + if (result == nsIAutoCompleteResult::RESULT_SUCCESS || 1.1380 + result == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { 1.1381 + // Try to autocomplete the default index for this search. 1.1382 + CompleteDefaultIndex(resultIndex); 1.1383 + } 1.1384 + 1.1385 + if (mSearchesOngoing == 0) { 1.1386 + // If this is the last search to return, cleanup. 1.1387 + PostSearchCleanup(); 1.1388 + } 1.1389 + 1.1390 + return NS_OK; 1.1391 +} 1.1392 + 1.1393 +nsresult 1.1394 +nsAutoCompleteController::PostSearchCleanup() 1.1395 +{ 1.1396 + NS_ENSURE_STATE(mInput); 1.1397 + nsCOMPtr<nsIAutoCompleteInput> input(mInput); 1.1398 + 1.1399 + uint32_t minResults; 1.1400 + input->GetMinResultsForPopup(&minResults); 1.1401 + 1.1402 + if (mRowCount || minResults == 0) { 1.1403 + OpenPopup(); 1.1404 + if (mRowCount) 1.1405 + mSearchStatus = nsIAutoCompleteController::STATUS_COMPLETE_MATCH; 1.1406 + else 1.1407 + mSearchStatus = nsIAutoCompleteController::STATUS_COMPLETE_NO_MATCH; 1.1408 + } else { 1.1409 + mSearchStatus = nsIAutoCompleteController::STATUS_COMPLETE_NO_MATCH; 1.1410 + ClosePopup(); 1.1411 + } 1.1412 + 1.1413 + // notify the input that the search is complete 1.1414 + input->OnSearchComplete(); 1.1415 + 1.1416 + return NS_OK; 1.1417 +} 1.1418 + 1.1419 +nsresult 1.1420 +nsAutoCompleteController::ClearResults() 1.1421 +{ 1.1422 + int32_t oldRowCount = mRowCount; 1.1423 + mRowCount = 0; 1.1424 + mResults.Clear(); 1.1425 + mMatchCounts.Clear(); 1.1426 + if (oldRowCount != 0) { 1.1427 + if (mTree) 1.1428 + mTree->RowCountChanged(0, -oldRowCount); 1.1429 + else if (mInput) { 1.1430 + nsCOMPtr<nsIAutoCompletePopup> popup; 1.1431 + mInput->GetPopup(getter_AddRefs(popup)); 1.1432 + NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE); 1.1433 + // if we had a tree, RowCountChanged() would have cleared the selection 1.1434 + // when the selected row was removed. But since we don't have a tree, 1.1435 + // we need to clear the selection manually. 1.1436 + popup->SetSelectedIndex(-1); 1.1437 + } 1.1438 + } 1.1439 + return NS_OK; 1.1440 +} 1.1441 + 1.1442 +nsresult 1.1443 +nsAutoCompleteController::CompleteDefaultIndex(int32_t aResultIndex) 1.1444 +{ 1.1445 + if (mDefaultIndexCompleted || mBackspaced || mSearchString.Length() == 0 || !mInput) 1.1446 + return NS_OK; 1.1447 + 1.1448 + int32_t selectionStart; 1.1449 + mInput->GetSelectionStart(&selectionStart); 1.1450 + int32_t selectionEnd; 1.1451 + mInput->GetSelectionEnd(&selectionEnd); 1.1452 + 1.1453 + // Don't try to automatically complete to the first result if there's already 1.1454 + // a selection or the cursor isn't at the end of the input 1.1455 + if (selectionEnd != selectionStart || 1.1456 + selectionEnd != (int32_t)mSearchString.Length()) 1.1457 + return NS_OK; 1.1458 + 1.1459 + bool shouldComplete; 1.1460 + mInput->GetCompleteDefaultIndex(&shouldComplete); 1.1461 + if (!shouldComplete) 1.1462 + return NS_OK; 1.1463 + 1.1464 + nsAutoString resultValue; 1.1465 + if (NS_SUCCEEDED(GetDefaultCompleteValue(aResultIndex, true, resultValue))) 1.1466 + CompleteValue(resultValue); 1.1467 + 1.1468 + mDefaultIndexCompleted = true; 1.1469 + 1.1470 + return NS_OK; 1.1471 +} 1.1472 + 1.1473 +nsresult 1.1474 +nsAutoCompleteController::GetDefaultCompleteResult(int32_t aResultIndex, 1.1475 + nsIAutoCompleteResult** _result, 1.1476 + int32_t* _defaultIndex) 1.1477 +{ 1.1478 + *_defaultIndex = -1; 1.1479 + int32_t resultIndex = aResultIndex; 1.1480 + 1.1481 + // If a result index was not provided, find the first defaultIndex result. 1.1482 + for (int32_t i = 0; resultIndex < 0 && i < mResults.Count(); ++i) { 1.1483 + nsIAutoCompleteResult *result = mResults[i]; 1.1484 + if (result && 1.1485 + NS_SUCCEEDED(result->GetDefaultIndex(_defaultIndex)) && 1.1486 + *_defaultIndex >= 0) { 1.1487 + resultIndex = i; 1.1488 + } 1.1489 + } 1.1490 + NS_ENSURE_TRUE(resultIndex >= 0, NS_ERROR_FAILURE); 1.1491 + 1.1492 + *_result = mResults.SafeObjectAt(resultIndex); 1.1493 + NS_ENSURE_TRUE(*_result, NS_ERROR_FAILURE); 1.1494 + 1.1495 + if (*_defaultIndex < 0) { 1.1496 + // The search must explicitly provide a default index in order 1.1497 + // for us to be able to complete. 1.1498 + (*_result)->GetDefaultIndex(_defaultIndex); 1.1499 + } 1.1500 + 1.1501 + if (*_defaultIndex < 0) { 1.1502 + // We were given a result index, but that result doesn't want to 1.1503 + // be autocompleted. 1.1504 + return NS_ERROR_FAILURE; 1.1505 + } 1.1506 + 1.1507 + // If the result wrongly notifies a RESULT_SUCCESS with no matches, or 1.1508 + // provides a defaultIndex greater than its matchCount, avoid trying to 1.1509 + // complete to an empty value. 1.1510 + uint32_t matchCount = 0; 1.1511 + (*_result)->GetMatchCount(&matchCount); 1.1512 + // Here defaultIndex is surely non-negative, so can be cast to unsigned. 1.1513 + if ((uint32_t)(*_defaultIndex) >= matchCount) { 1.1514 + return NS_ERROR_FAILURE; 1.1515 + } 1.1516 + 1.1517 + return NS_OK; 1.1518 +} 1.1519 + 1.1520 +nsresult 1.1521 +nsAutoCompleteController::GetDefaultCompleteValue(int32_t aResultIndex, 1.1522 + bool aPreserveCasing, 1.1523 + nsAString &_retval) 1.1524 +{ 1.1525 + nsIAutoCompleteResult *result; 1.1526 + int32_t defaultIndex = -1; 1.1527 + nsresult rv = GetDefaultCompleteResult(aResultIndex, &result, &defaultIndex); 1.1528 + if (NS_FAILED(rv)) return rv; 1.1529 + 1.1530 + nsAutoString resultValue; 1.1531 + result->GetValueAt(defaultIndex, resultValue); 1.1532 + if (aPreserveCasing && 1.1533 + StringBeginsWith(resultValue, mSearchString, 1.1534 + nsCaseInsensitiveStringComparator())) { 1.1535 + // We try to preserve user casing, otherwise we would end up changing 1.1536 + // the case of what he typed, if we have a result with a different casing. 1.1537 + // For example if we have result "Test", and user starts writing "tuna", 1.1538 + // after digiting t, we would convert it to T trying to autocomplete "Test". 1.1539 + // We will still complete to cased "Test" if the user explicitely choose 1.1540 + // that result, by either selecting it in the results popup, or with 1.1541 + // keyboard navigation or if autocompleting in the middle. 1.1542 + nsAutoString casedResultValue; 1.1543 + casedResultValue.Assign(mSearchString); 1.1544 + // Use what the user has typed so far. 1.1545 + casedResultValue.Append(Substring(resultValue, 1.1546 + mSearchString.Length(), 1.1547 + resultValue.Length())); 1.1548 + _retval = casedResultValue; 1.1549 + } 1.1550 + else 1.1551 + _retval = resultValue; 1.1552 + 1.1553 + return NS_OK; 1.1554 +} 1.1555 + 1.1556 +nsresult 1.1557 +nsAutoCompleteController::GetFinalDefaultCompleteValue(nsAString &_retval) 1.1558 +{ 1.1559 + MOZ_ASSERT(mInput, "Must have a valid input"); 1.1560 + nsIAutoCompleteResult *result; 1.1561 + int32_t defaultIndex = -1; 1.1562 + nsresult rv = GetDefaultCompleteResult(-1, &result, &defaultIndex); 1.1563 + if (NS_FAILED(rv)) return rv; 1.1564 + 1.1565 + result->GetValueAt(defaultIndex, _retval); 1.1566 + nsAutoString inputValue; 1.1567 + mInput->GetTextValue(inputValue); 1.1568 + if (!_retval.Equals(inputValue, nsCaseInsensitiveStringComparator())) { 1.1569 + return NS_ERROR_FAILURE; 1.1570 + } 1.1571 + 1.1572 + nsAutoString finalCompleteValue; 1.1573 + rv = result->GetFinalCompleteValueAt(defaultIndex, finalCompleteValue); 1.1574 + if (NS_SUCCEEDED(rv)) { 1.1575 + _retval = finalCompleteValue; 1.1576 + } 1.1577 + 1.1578 + MOZ_ASSERT(FindInReadable(inputValue, _retval, nsCaseInsensitiveStringComparator()), 1.1579 + "Return value must include input value."); 1.1580 + return NS_OK; 1.1581 +} 1.1582 + 1.1583 +nsresult 1.1584 +nsAutoCompleteController::CompleteValue(nsString &aValue) 1.1585 +/* mInput contains mSearchString, which we want to autocomplete to aValue. If 1.1586 + * selectDifference is true, select the remaining portion of aValue not 1.1587 + * contained in mSearchString. */ 1.1588 +{ 1.1589 + MOZ_ASSERT(mInput, "Must have a valid input"); 1.1590 + const int32_t mSearchStringLength = mSearchString.Length(); 1.1591 + int32_t endSelect = aValue.Length(); // By default, select all of aValue. 1.1592 + 1.1593 + if (aValue.IsEmpty() || 1.1594 + StringBeginsWith(aValue, mSearchString, 1.1595 + nsCaseInsensitiveStringComparator())) { 1.1596 + // aValue is empty (we were asked to clear mInput), or mSearchString 1.1597 + // matches the beginning of aValue. In either case we can simply 1.1598 + // autocomplete to aValue. 1.1599 + mInput->SetTextValue(aValue); 1.1600 + } else { 1.1601 + nsresult rv; 1.1602 + nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); 1.1603 + NS_ENSURE_SUCCESS(rv, rv); 1.1604 + nsAutoCString scheme; 1.1605 + if (NS_SUCCEEDED(ios->ExtractScheme(NS_ConvertUTF16toUTF8(aValue), scheme))) { 1.1606 + // Trying to autocomplete a URI from somewhere other than the beginning. 1.1607 + // Only succeed if the missing portion is "http://"; otherwise do not 1.1608 + // autocomplete. This prevents us from "helpfully" autocompleting to a 1.1609 + // URI that isn't equivalent to what the user expected. 1.1610 + const int32_t findIndex = 7; // length of "http://" 1.1611 + 1.1612 + if ((endSelect < findIndex + mSearchStringLength) || 1.1613 + !scheme.LowerCaseEqualsLiteral("http") || 1.1614 + !Substring(aValue, findIndex, mSearchStringLength).Equals( 1.1615 + mSearchString, nsCaseInsensitiveStringComparator())) { 1.1616 + return NS_OK; 1.1617 + } 1.1618 + 1.1619 + mInput->SetTextValue(mSearchString + 1.1620 + Substring(aValue, mSearchStringLength + findIndex, 1.1621 + endSelect)); 1.1622 + 1.1623 + endSelect -= findIndex; // We're skipping this many characters of aValue. 1.1624 + } else { 1.1625 + // Autocompleting something other than a URI from the middle. 1.1626 + // Use the format "searchstring >> full string" to indicate to the user 1.1627 + // what we are going to replace their search string with. 1.1628 + mInput->SetTextValue(mSearchString + NS_LITERAL_STRING(" >> ") + aValue); 1.1629 + 1.1630 + endSelect = mSearchString.Length() + 4 + aValue.Length(); 1.1631 + } 1.1632 + } 1.1633 + 1.1634 + mInput->SelectTextRange(mSearchStringLength, endSelect); 1.1635 + 1.1636 + return NS_OK; 1.1637 +} 1.1638 + 1.1639 +nsresult 1.1640 +nsAutoCompleteController::GetResultLabelAt(int32_t aIndex, nsAString & _retval) 1.1641 +{ 1.1642 + return GetResultValueLabelAt(aIndex, false, false, _retval); 1.1643 +} 1.1644 + 1.1645 +nsresult 1.1646 +nsAutoCompleteController::GetResultValueAt(int32_t aIndex, bool aGetFinalValue, 1.1647 + nsAString & _retval) 1.1648 +{ 1.1649 + return GetResultValueLabelAt(aIndex, aGetFinalValue, true, _retval); 1.1650 +} 1.1651 + 1.1652 +nsresult 1.1653 +nsAutoCompleteController::GetResultValueLabelAt(int32_t aIndex, 1.1654 + bool aGetFinalValue, 1.1655 + bool aGetValue, 1.1656 + nsAString & _retval) 1.1657 +{ 1.1658 + NS_ENSURE_TRUE(aIndex >= 0 && (uint32_t) aIndex < mRowCount, NS_ERROR_ILLEGAL_VALUE); 1.1659 + 1.1660 + int32_t rowIndex; 1.1661 + nsIAutoCompleteResult *result; 1.1662 + nsresult rv = GetResultAt(aIndex, &result, &rowIndex); 1.1663 + NS_ENSURE_SUCCESS(rv, rv); 1.1664 + 1.1665 + uint16_t searchResult; 1.1666 + result->GetSearchResult(&searchResult); 1.1667 + 1.1668 + if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) { 1.1669 + if (aGetValue) 1.1670 + return NS_ERROR_FAILURE; 1.1671 + result->GetErrorDescription(_retval); 1.1672 + } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || 1.1673 + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { 1.1674 + if (aGetFinalValue) 1.1675 + result->GetFinalCompleteValueAt(rowIndex, _retval); 1.1676 + else if (aGetValue) 1.1677 + result->GetValueAt(rowIndex, _retval); 1.1678 + else 1.1679 + result->GetLabelAt(rowIndex, _retval); 1.1680 + } 1.1681 + 1.1682 + return NS_OK; 1.1683 +} 1.1684 + 1.1685 +/** 1.1686 + * Given the index of a row in the autocomplete popup, find the 1.1687 + * corresponding nsIAutoCompleteSearch index, and sub-index into 1.1688 + * the search's results list. 1.1689 + */ 1.1690 +nsresult 1.1691 +nsAutoCompleteController::RowIndexToSearch(int32_t aRowIndex, int32_t *aSearchIndex, int32_t *aItemIndex) 1.1692 +{ 1.1693 + *aSearchIndex = -1; 1.1694 + *aItemIndex = -1; 1.1695 + 1.1696 + uint32_t index = 0; 1.1697 + 1.1698 + // Move index through the results of each registered nsIAutoCompleteSearch 1.1699 + // until we find the given row 1.1700 + for (uint32_t i = 0; i < mSearches.Length(); ++i) { 1.1701 + nsIAutoCompleteResult *result = mResults.SafeObjectAt(i); 1.1702 + if (!result) 1.1703 + continue; 1.1704 + 1.1705 + uint32_t rowCount = 0; 1.1706 + 1.1707 + // Skip past the result completely if it is marked as hidden 1.1708 + bool isTypeAheadResult = false; 1.1709 + result->GetTypeAheadResult(&isTypeAheadResult); 1.1710 + 1.1711 + if (!isTypeAheadResult) { 1.1712 + uint16_t searchResult; 1.1713 + result->GetSearchResult(&searchResult); 1.1714 + 1.1715 + // Find out how many results were provided by the 1.1716 + // current nsIAutoCompleteSearch. 1.1717 + if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || 1.1718 + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { 1.1719 + result->GetMatchCount(&rowCount); 1.1720 + } 1.1721 + } 1.1722 + 1.1723 + // If the given row index is within the results range 1.1724 + // of the current nsIAutoCompleteSearch then return the 1.1725 + // search index and sub-index into the results array 1.1726 + if ((rowCount != 0) && (index + rowCount-1 >= (uint32_t) aRowIndex)) { 1.1727 + *aSearchIndex = i; 1.1728 + *aItemIndex = aRowIndex - index; 1.1729 + return NS_OK; 1.1730 + } 1.1731 + 1.1732 + // Advance the popup table index cursor past the 1.1733 + // results of the current search. 1.1734 + index += rowCount; 1.1735 + } 1.1736 + 1.1737 + return NS_OK; 1.1738 +} 1.1739 + 1.1740 +NS_GENERIC_FACTORY_CONSTRUCTOR(nsAutoCompleteController) 1.1741 +NS_GENERIC_FACTORY_CONSTRUCTOR(nsAutoCompleteSimpleResult) 1.1742 + 1.1743 +NS_DEFINE_NAMED_CID(NS_AUTOCOMPLETECONTROLLER_CID); 1.1744 +NS_DEFINE_NAMED_CID(NS_AUTOCOMPLETESIMPLERESULT_CID); 1.1745 + 1.1746 +static const mozilla::Module::CIDEntry kAutoCompleteCIDs[] = { 1.1747 + { &kNS_AUTOCOMPLETECONTROLLER_CID, false, nullptr, nsAutoCompleteControllerConstructor }, 1.1748 + { &kNS_AUTOCOMPLETESIMPLERESULT_CID, false, nullptr, nsAutoCompleteSimpleResultConstructor }, 1.1749 + { nullptr } 1.1750 +}; 1.1751 + 1.1752 +static const mozilla::Module::ContractIDEntry kAutoCompleteContracts[] = { 1.1753 + { NS_AUTOCOMPLETECONTROLLER_CONTRACTID, &kNS_AUTOCOMPLETECONTROLLER_CID }, 1.1754 + { NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID, &kNS_AUTOCOMPLETESIMPLERESULT_CID }, 1.1755 + { nullptr } 1.1756 +}; 1.1757 + 1.1758 +static const mozilla::Module kAutoCompleteModule = { 1.1759 + mozilla::Module::kVersion, 1.1760 + kAutoCompleteCIDs, 1.1761 + kAutoCompleteContracts 1.1762 +}; 1.1763 + 1.1764 +NSMODULE_DEFN(tkAutoCompleteModule) = &kAutoCompleteModule;