LCOV - code coverage report
Current view: directory - toolkit/components/autocomplete - nsAutoCompleteController.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 754 506 67.1 %
Date: 2012-06-02 Functions: 89 50 56.2 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla Communicator client code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Joe Hewitt <hewitt@netscape.com> (Original Author)
      24                 :  *   Dean Tessman <dean_tessman@hotmail.com>
      25                 :  *   Johnny Stenback <jst@mozilla.jstenback.com>
      26                 :  *   Masayuki Nakano <masayuki@d-toybox.com>
      27                 :  *   Michael Ventnor <m.ventnor@gmail.com>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      31                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #include "nsAutoCompleteController.h"
      44                 : #include "nsAutoCompleteSimpleResult.h"
      45                 : 
      46                 : #include "nsAutoPtr.h"
      47                 : #include "nsNetCID.h"
      48                 : #include "nsIIOService.h"
      49                 : #include "nsToolkitCompsCID.h"
      50                 : #include "nsIServiceManager.h"
      51                 : #include "nsIAtomService.h"
      52                 : #include "nsReadableUtils.h"
      53                 : #include "nsUnicharUtils.h"
      54                 : #include "nsITreeColumns.h"
      55                 : #include "nsIObserverService.h"
      56                 : #include "nsIDOMKeyEvent.h"
      57                 : #include "mozilla/Services.h"
      58                 : #include "mozilla/ModuleUtils.h"
      59                 : 
      60                 : static const char *kAutoCompleteSearchCID = "@mozilla.org/autocomplete/search;1?name=";
      61                 : 
      62            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsAutoCompleteController)
      63              36 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAutoCompleteController)
      64              36 :   tmp->SetInput(nsnull);
      65              36 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      66              36 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAutoCompleteController)
      67              36 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInput)
      68              36 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mSearches)
      69              36 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mResults)
      70              36 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      71                 : 
      72            2056 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAutoCompleteController)
      73            2056 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAutoCompleteController)
      74            3412 : NS_INTERFACE_TABLE_HEAD(nsAutoCompleteController)
      75            3412 :   NS_INTERFACE_TABLE4(nsAutoCompleteController, nsIAutoCompleteController,
      76                 :                       nsIAutoCompleteObserver, nsITimerCallback, nsITreeView)
      77            3412 :   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAutoCompleteController)
      78             860 : NS_INTERFACE_MAP_END
      79                 : 
      80              41 : nsAutoCompleteController::nsAutoCompleteController() :
      81                 :   mDefaultIndexCompleted(false),
      82                 :   mBackspaced(false),
      83                 :   mPopupClosedByCompositionStart(false),
      84                 :   mCompositionState(eCompositionState_None),
      85                 :   mSearchStatus(nsAutoCompleteController::STATUS_NONE),
      86                 :   mRowCount(0),
      87                 :   mSearchesOngoing(0),
      88                 :   mSearchesFailed(0),
      89                 :   mFirstSearchResult(false),
      90              41 :   mImmediateSearchesCount(0)
      91                 : {
      92              41 : }
      93                 : 
      94             123 : nsAutoCompleteController::~nsAutoCompleteController()
      95                 : {
      96              41 :   SetInput(nsnull);
      97             164 : }
      98                 : 
      99                 : ////////////////////////////////////////////////////////////////////////
     100                 : //// nsIAutoCompleteController
     101                 : 
     102                 : NS_IMETHODIMP
     103             206 : nsAutoCompleteController::GetSearchStatus(PRUint16 *aSearchStatus)
     104                 : {
     105             206 :   *aSearchStatus = mSearchStatus;
     106             206 :   return NS_OK;
     107                 : }
     108                 : 
     109                 : NS_IMETHODIMP
     110            1008 : nsAutoCompleteController::GetMatchCount(PRUint32 *aMatchCount)
     111                 : {
     112            1008 :   *aMatchCount = mRowCount;
     113            1008 :   return NS_OK;
     114                 : }
     115                 : 
     116                 : NS_IMETHODIMP
     117               5 : nsAutoCompleteController::GetInput(nsIAutoCompleteInput **aInput)
     118                 : {
     119               5 :   *aInput = mInput;
     120               5 :   NS_IF_ADDREF(*aInput);
     121               5 :   return NS_OK;
     122                 : }
     123                 : 
     124                 : NS_IMETHODIMP
     125             319 : nsAutoCompleteController::SetInput(nsIAutoCompleteInput *aInput)
     126                 : {
     127                 :   // Don't do anything if the input isn't changing.
     128             319 :   if (mInput == aInput)
     129              38 :     return NS_OK;
     130                 : 
     131                 :   // Clear out the current search context
     132             281 :   if (mInput) {
     133                 :     // Stop all searches in case they are async.
     134             241 :     StopSearch();
     135             241 :     ClearResults();
     136             241 :     ClosePopup();
     137             241 :     mSearches.Clear();
     138                 :   }
     139                 : 
     140             281 :   mInput = aInput;
     141                 : 
     142                 :   // Nothing more to do if the input was just being set to null.
     143             281 :   if (!aInput)
     144              40 :     return NS_OK;
     145                 : 
     146             482 :   nsAutoString newValue;
     147             241 :   aInput->GetTextValue(newValue);
     148                 : 
     149                 :   // Clear out this reference in case the new input's popup has no tree
     150             241 :   mTree = nsnull;
     151                 : 
     152                 :   // Reset all search state members to default values
     153             241 :   mSearchString = newValue;
     154             241 :   mDefaultIndexCompleted = false;
     155             241 :   mBackspaced = false;
     156             241 :   mSearchStatus = nsIAutoCompleteController::STATUS_NONE;
     157             241 :   mRowCount = 0;
     158             241 :   mSearchesOngoing = 0;
     159                 : 
     160                 :   // Initialize our list of search objects
     161                 :   PRUint32 searchCount;
     162             241 :   aInput->GetSearchCount(&searchCount);
     163             241 :   mResults.SetCapacity(searchCount);
     164             241 :   mSearches.SetCapacity(searchCount);
     165             241 :   mMatchCounts.SetLength(searchCount);
     166             241 :   mImmediateSearchesCount = 0;
     167                 : 
     168             241 :   const char *searchCID = kAutoCompleteSearchCID;
     169                 : 
     170             490 :   for (PRUint32 i = 0; i < searchCount; ++i) {
     171                 :     // Use the search name to create the contract id string for the search service
     172             498 :     nsCAutoString searchName;
     173             249 :     aInput->GetSearchAt(i, searchName);
     174             498 :     nsCAutoString cid(searchCID);
     175             249 :     cid.Append(searchName);
     176                 : 
     177                 :     // Use the created cid to get a pointer to the search service and store it for later
     178             498 :     nsCOMPtr<nsIAutoCompleteSearch> search = do_GetService(cid.get());
     179             249 :     if (search) {
     180             249 :       mSearches.AppendObject(search);
     181                 : 
     182                 :       // Count immediate searches.
     183             249 :       PRUint16 searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
     184                 :       nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
     185             498 :         do_QueryInterface(search);
     186             249 :       if (searchDesc && NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) &&
     187                 :           searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE)
     188              29 :         mImmediateSearchesCount++;
     189                 :     }
     190                 :   }
     191                 : 
     192             241 :   return NS_OK;
     193                 : }
     194                 : 
     195                 : NS_IMETHODIMP
     196             248 : nsAutoCompleteController::StartSearch(const nsAString &aSearchString)
     197                 : {
     198             248 :   mSearchString = aSearchString;
     199             248 :   StartSearches();
     200             248 :   return NS_OK;
     201                 : }
     202                 : 
     203                 : NS_IMETHODIMP
     204               2 : nsAutoCompleteController::HandleText()
     205                 : {
     206                 :   // Note: the events occur in the following order when IME is used.
     207                 :   // 1. a compositionstart event(HandleStartComposition)
     208                 :   // 2. some input events (HandleText), eCompositionState_Composing
     209                 :   // 3. a compositionend event(HandleEndComposition)
     210                 :   // 4. an input event(HandleText), eCompositionState_Committing
     211                 :   // We should do nothing during composition.
     212               2 :   if (mCompositionState == eCompositionState_Composing) {
     213               0 :     return NS_OK;
     214                 :   }
     215                 : 
     216                 :   bool handlingCompositionCommit =
     217               2 :     (mCompositionState == eCompositionState_Committing);
     218               2 :   bool popupClosedByCompositionStart = mPopupClosedByCompositionStart;
     219               2 :   if (handlingCompositionCommit) {
     220               1 :     mCompositionState = eCompositionState_None;
     221               1 :     mPopupClosedByCompositionStart = false;
     222                 :   }
     223                 : 
     224               2 :   if (!mInput) {
     225                 :     // Stop all searches in case they are async.
     226               0 :     StopSearch();
     227                 :     // Note: if now is after blur and IME end composition,
     228                 :     // check mInput before calling.
     229                 :     // See https://bugzilla.mozilla.org/show_bug.cgi?id=193544#c31
     230               0 :     NS_ERROR("Called before attaching to the control or after detaching from the control");
     231               0 :     return NS_OK;
     232                 :   }
     233                 : 
     234               4 :   nsAutoString newValue;
     235               4 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
     236               2 :   input->GetTextValue(newValue);
     237                 : 
     238                 :   // Stop all searches in case they are async.
     239               2 :   StopSearch();
     240                 : 
     241               2 :   if (!mInput) {
     242                 :     // StopSearch() can call PostSearchCleanup() which might result
     243                 :     // in a blur event, which could null out mInput, so we need to check it
     244                 :     // again.  See bug #395344 for more details
     245               0 :     return NS_OK;
     246                 :   }
     247                 : 
     248                 :   bool disabled;
     249               2 :   input->GetDisableAutoComplete(&disabled);
     250               2 :   NS_ENSURE_TRUE(!disabled, NS_OK);
     251                 : 
     252                 :   // Don't search again if the new string is the same as the last search
     253                 :   // However, if this is called immediately after compositionend event,
     254                 :   // we need to search the same value again since the search was canceled
     255                 :   // at compositionstart event handler.
     256               3 :   if (!handlingCompositionCommit && newValue.Length() > 0 &&
     257               1 :       newValue.Equals(mSearchString)) {
     258               0 :     return NS_OK;
     259                 :   }
     260                 : 
     261                 :   // Determine if the user has removed text from the end (probably by backspacing)
     262               8 :   if (newValue.Length() < mSearchString.Length() &&
     263               6 :       Substring(mSearchString, 0, newValue.Length()).Equals(newValue))
     264                 :   {
     265                 :     // We need to throw away previous results so we don't try to search through them again
     266               2 :     ClearResults();
     267               2 :     mBackspaced = true;
     268                 :   } else
     269               0 :     mBackspaced = false;
     270                 : 
     271               2 :   mSearchString = newValue;
     272                 : 
     273                 :   // Don't search if the value is empty
     274               2 :   if (newValue.Length() == 0) {
     275                 :     // If autocomplete popup was closed by compositionstart event handler,
     276                 :     // we should reopen it forcibly even if the value is empty.
     277               0 :     if (popupClosedByCompositionStart && handlingCompositionCommit) {
     278                 :       bool cancel;
     279               0 :       HandleKeyNavigation(nsIDOMKeyEvent::DOM_VK_DOWN, &cancel);
     280               0 :       return NS_OK;
     281                 :     }
     282               0 :     ClosePopup();
     283               0 :     return NS_OK;
     284                 :   }
     285                 : 
     286               2 :   StartSearches();
     287                 : 
     288               2 :   return NS_OK;
     289                 : }
     290                 : 
     291                 : NS_IMETHODIMP
     292              11 : nsAutoCompleteController::HandleEnter(bool aIsPopupSelection, bool *_retval)
     293                 : {
     294              11 :   *_retval = false;
     295              11 :   if (!mInput)
     296               0 :     return NS_OK;
     297                 : 
     298                 :   // allow the event through unless there is something selected in the popup
     299              11 :   mInput->GetPopupOpen(_retval);
     300              11 :   if (*_retval) {
     301              18 :     nsCOMPtr<nsIAutoCompletePopup> popup;
     302               9 :     mInput->GetPopup(getter_AddRefs(popup));
     303                 : 
     304               9 :     if (popup) {
     305                 :       PRInt32 selectedIndex;
     306               9 :       popup->GetSelectedIndex(&selectedIndex);
     307               9 :       *_retval = selectedIndex >= 0;
     308                 :     }
     309                 :   }
     310                 : 
     311                 :   // Stop the search, and handle the enter.
     312              11 :   StopSearch();
     313              11 :   EnterMatch(aIsPopupSelection);
     314                 : 
     315              11 :   return NS_OK;
     316                 : }
     317                 : 
     318                 : NS_IMETHODIMP
     319               1 : nsAutoCompleteController::HandleEscape(bool *_retval)
     320                 : {
     321               1 :   *_retval = false;
     322               1 :   if (!mInput)
     323               0 :     return NS_OK;
     324                 : 
     325                 :   // allow the event through if the popup is closed
     326               1 :   mInput->GetPopupOpen(_retval);
     327                 : 
     328                 :   // Stop all searches in case they are async.
     329               1 :   StopSearch();
     330               1 :   ClearResults();
     331               1 :   RevertTextValue();
     332               1 :   ClosePopup();
     333                 : 
     334               1 :   return NS_OK;
     335                 : }
     336                 : 
     337                 : NS_IMETHODIMP
     338               1 : nsAutoCompleteController::HandleStartComposition()
     339                 : {
     340               1 :   NS_ENSURE_TRUE(mCompositionState != eCompositionState_Composing, NS_OK);
     341                 : 
     342               1 :   mPopupClosedByCompositionStart = false;
     343               1 :   mCompositionState = eCompositionState_Composing;
     344                 : 
     345               1 :   if (!mInput)
     346               0 :     return NS_OK;
     347                 : 
     348               2 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
     349                 :   bool disabled;
     350               1 :   input->GetDisableAutoComplete(&disabled);
     351               1 :   if (disabled)
     352               0 :     return NS_OK;
     353                 : 
     354                 :   // Stop all searches in case they are async.
     355               1 :   StopSearch();
     356                 : 
     357               1 :   bool isOpen = false;
     358               1 :   input->GetPopupOpen(&isOpen);
     359               1 :   if (isOpen) {
     360               0 :     ClosePopup();
     361                 : 
     362               0 :     bool stillOpen = false;
     363               0 :     input->GetPopupOpen(&stillOpen);
     364               0 :     mPopupClosedByCompositionStart = !stillOpen;
     365                 :   }
     366               1 :   return NS_OK;
     367                 : }
     368                 : 
     369                 : NS_IMETHODIMP
     370               1 : nsAutoCompleteController::HandleEndComposition()
     371                 : {
     372               1 :   NS_ENSURE_TRUE(mCompositionState == eCompositionState_Composing, NS_OK);
     373                 : 
     374                 :   // We can't yet retrieve the committed value from the editor, since it isn't
     375                 :   // completely committed yet. Set mCompositionState to
     376                 :   // eCompositionState_Committing, so that when HandleText() is called (in
     377                 :   // response to the "input" event), we know that we should handle the
     378                 :   // committed text.
     379               1 :   mCompositionState = eCompositionState_Committing;
     380               1 :   return NS_OK;
     381                 : }
     382                 : 
     383                 : NS_IMETHODIMP
     384               1 : nsAutoCompleteController::HandleTab()
     385                 : {
     386                 :   bool cancel;
     387               1 :   return HandleEnter(false, &cancel);
     388                 : }
     389                 : 
     390                 : NS_IMETHODIMP
     391               2 : nsAutoCompleteController::HandleKeyNavigation(PRUint32 aKey, bool *_retval)
     392                 : {
     393                 :   // By default, don't cancel the event
     394               2 :   *_retval = false;
     395                 : 
     396               2 :   if (!mInput) {
     397                 :     // Stop all searches in case they are async.
     398               0 :     StopSearch();
     399                 :     // Note: if now is after blur and IME end composition,
     400                 :     // check mInput before calling.
     401                 :     // See https://bugzilla.mozilla.org/show_bug.cgi?id=193544#c31
     402               0 :     NS_ERROR("Called before attaching to the control or after detaching from the control");
     403               0 :     return NS_OK;
     404                 :   }
     405                 : 
     406               4 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
     407               4 :   nsCOMPtr<nsIAutoCompletePopup> popup;
     408               2 :   input->GetPopup(getter_AddRefs(popup));
     409               2 :   NS_ENSURE_TRUE(popup != nsnull, NS_ERROR_FAILURE);
     410                 : 
     411                 :   bool disabled;
     412               2 :   input->GetDisableAutoComplete(&disabled);
     413               2 :   NS_ENSURE_TRUE(!disabled, NS_OK);
     414                 : 
     415               2 :   if (aKey == nsIDOMKeyEvent::DOM_VK_UP ||
     416                 :       aKey == nsIDOMKeyEvent::DOM_VK_DOWN ||
     417                 :       aKey == nsIDOMKeyEvent::DOM_VK_PAGE_UP ||
     418                 :       aKey == nsIDOMKeyEvent::DOM_VK_PAGE_DOWN)
     419                 :   {
     420                 :     // Prevent the input from handling up/down events, as it may move
     421                 :     // the cursor to home/end on some systems
     422               1 :     *_retval = true;
     423                 : 
     424               1 :     bool isOpen = false;
     425               1 :     input->GetPopupOpen(&isOpen);
     426               1 :     if (isOpen) {
     427                 :       bool reverse = aKey == nsIDOMKeyEvent::DOM_VK_UP ||
     428               0 :                       aKey == nsIDOMKeyEvent::DOM_VK_PAGE_UP ? true : false;
     429                 :       bool page = aKey == nsIDOMKeyEvent::DOM_VK_PAGE_UP ||
     430               0 :                     aKey == nsIDOMKeyEvent::DOM_VK_PAGE_DOWN ? true : false;
     431                 : 
     432                 :       // Fill in the value of the textbox with whatever is selected in the popup
     433                 :       // if the completeSelectedIndex attribute is set.  We check this before
     434                 :       // calling SelectBy of an earlier attempt to avoid crashing.
     435                 :       bool completeSelection;
     436               0 :       input->GetCompleteSelectedIndex(&completeSelection);
     437                 : 
     438                 :       // Instruct the result view to scroll by the given amount and direction
     439               0 :       popup->SelectBy(reverse, page);
     440                 : 
     441               0 :       if (completeSelection)
     442                 :       {
     443                 :         PRInt32 selectedIndex;
     444               0 :         popup->GetSelectedIndex(&selectedIndex);
     445               0 :         if (selectedIndex >= 0) {
     446                 :           //  A result is selected, so fill in its value
     447               0 :           nsAutoString value;
     448               0 :           if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, true, value))) {
     449               0 :             input->SetTextValue(value);
     450               0 :             input->SelectTextRange(value.Length(), value.Length());
     451                 :           }
     452                 :         } else {
     453                 :           // Nothing is selected, so fill in the last typed value
     454               0 :           input->SetTextValue(mSearchString);
     455               0 :           input->SelectTextRange(mSearchString.Length(), mSearchString.Length());
     456                 :         }
     457                 :       }
     458                 :     } else {
     459                 : #ifdef XP_MACOSX
     460                 :       // on Mac, only show the popup if the caret is at the start or end of
     461                 :       // the input and there is no selection, so that the default defined key
     462                 :       // shortcuts for up and down move to the beginning and end of the field
     463                 :       // otherwise.
     464                 :       PRInt32 start, end;
     465                 :       if (aKey == nsIDOMKeyEvent::DOM_VK_UP) {
     466                 :         input->GetSelectionStart(&start);
     467                 :         input->GetSelectionEnd(&end);
     468                 :         if (start > 0 || start != end)
     469                 :           *_retval = false;
     470                 :       }
     471                 :       else if (aKey == nsIDOMKeyEvent::DOM_VK_DOWN) {
     472                 :         nsAutoString text;
     473                 :         input->GetTextValue(text);
     474                 :         input->GetSelectionStart(&start);
     475                 :         input->GetSelectionEnd(&end);
     476                 :         if (start != end || end < (PRInt32)text.Length())
     477                 :           *_retval = false;
     478                 :       }
     479                 : #endif
     480               1 :       if (*_retval) {
     481                 :         // Open the popup if there has been a previous search, or else kick off a new search
     482               1 :         if (mResults.Count() > 0) {
     483               0 :           if (mRowCount) {
     484               0 :             OpenPopup();
     485                 :           }
     486                 :         } else {
     487                 :           // Stop all searches in case they are async.
     488               1 :           StopSearch();
     489                 : 
     490               1 :           if (!mInput) {
     491                 :             // StopSearch() can call PostSearchCleanup() which might result
     492                 :             // in a blur event, which could null out mInput, so we need to check it
     493                 :             // again.  See bug #395344 for more details
     494               1 :             return NS_OK;
     495                 :           }
     496                 : 
     497               0 :           StartSearches();
     498                 :         }
     499                 :       }
     500               0 :     }
     501               1 :   } else if (   aKey == nsIDOMKeyEvent::DOM_VK_LEFT
     502                 :              || aKey == nsIDOMKeyEvent::DOM_VK_RIGHT
     503                 : #ifndef XP_MACOSX
     504                 :              || aKey == nsIDOMKeyEvent::DOM_VK_HOME
     505                 : #endif
     506                 :             )
     507                 :   {
     508                 :     // The user hit a text-navigation key.
     509               1 :     bool isOpen = false;
     510               1 :     input->GetPopupOpen(&isOpen);
     511               1 :     if (isOpen) {
     512                 :       PRInt32 selectedIndex;
     513               1 :       popup->GetSelectedIndex(&selectedIndex);
     514                 :       bool shouldComplete;
     515               1 :       input->GetCompleteDefaultIndex(&shouldComplete);
     516               1 :       if (selectedIndex >= 0) {
     517                 :         // The pop-up is open and has a selection, take its value
     518               0 :         nsAutoString value;
     519               0 :         if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, true, value))) {
     520               0 :           input->SetTextValue(value);
     521               0 :           input->SelectTextRange(value.Length(), value.Length());
     522                 :         }
     523                 :       }
     524               1 :       else if (shouldComplete) {
     525                 :         // We usually try to preserve the casing of what user has typed, but
     526                 :         // if he wants to autocomplete, we will replace the value with the
     527                 :         // actual autocomplete result.
     528                 :         // The user wants explicitely to use that result, so this ensures
     529                 :         // association of the result with the autocompleted text.
     530               2 :         nsAutoString value;
     531               2 :         nsAutoString inputValue;
     532               1 :         input->GetTextValue(inputValue);
     533               2 :         if (NS_SUCCEEDED(GetDefaultCompleteValue(-1, false, value)) &&
     534               1 :             value.Equals(inputValue, nsCaseInsensitiveStringComparator())) {
     535               1 :           input->SetTextValue(value);
     536               1 :           input->SelectTextRange(value.Length(), value.Length());
     537                 :         }
     538                 :       }
     539                 :       // Close the pop-up even if nothing was selected
     540               1 :       ClearSearchTimer();
     541               1 :       ClosePopup();
     542                 :     }
     543                 :     // Update last-searched string to the current input, since the input may
     544                 :     // have changed.  Without this, subsequent backspaces look like text
     545                 :     // additions, not text deletions.
     546               2 :     nsAutoString value;
     547               1 :     input->GetTextValue(value);
     548               1 :     mSearchString = value;
     549                 :   }
     550                 : 
     551               1 :   return NS_OK;
     552                 : }
     553                 : 
     554                 : NS_IMETHODIMP
     555               0 : nsAutoCompleteController::HandleDelete(bool *_retval)
     556                 : {
     557               0 :   *_retval = false;
     558               0 :   if (!mInput)
     559               0 :     return NS_OK;
     560                 : 
     561               0 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
     562               0 :   bool isOpen = false;
     563               0 :   input->GetPopupOpen(&isOpen);
     564               0 :   if (!isOpen || mRowCount <= 0) {
     565                 :     // Nothing left to delete, proceed as normal
     566               0 :     HandleText();
     567               0 :     return NS_OK;
     568                 :   }
     569                 : 
     570               0 :   nsCOMPtr<nsIAutoCompletePopup> popup;
     571               0 :   input->GetPopup(getter_AddRefs(popup));
     572                 : 
     573                 :   PRInt32 index, searchIndex, rowIndex;
     574               0 :   popup->GetSelectedIndex(&index);
     575               0 :   RowIndexToSearch(index, &searchIndex, &rowIndex);
     576               0 :   NS_ENSURE_TRUE(searchIndex >= 0 && rowIndex >= 0, NS_ERROR_FAILURE);
     577                 : 
     578               0 :   nsIAutoCompleteResult *result = mResults[searchIndex];
     579               0 :   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
     580                 : 
     581               0 :   nsAutoString search;
     582               0 :   input->GetSearchParam(search);
     583                 : 
     584                 :   // Clear the row in our result and in the DB.
     585               0 :   result->RemoveValueAt(rowIndex, true);
     586               0 :   --mRowCount;
     587                 : 
     588                 :   // We removed it, so make sure we cancel the event that triggered this call.
     589               0 :   *_retval = true;
     590                 : 
     591                 :   // Unselect the current item.
     592               0 :   popup->SetSelectedIndex(-1);
     593                 : 
     594                 :   // Tell the tree that the row count changed.
     595               0 :   if (mTree)
     596               0 :     mTree->RowCountChanged(mRowCount, -1);
     597                 : 
     598                 :   // Adjust index, if needed.
     599               0 :   if (index >= (PRInt32)mRowCount)
     600               0 :     index = mRowCount - 1;
     601                 : 
     602               0 :   if (mRowCount > 0) {
     603                 :     // There are still rows in the popup, select the current index again.
     604               0 :     popup->SetSelectedIndex(index);
     605                 : 
     606                 :     // Complete to the new current value.
     607               0 :     bool shouldComplete = false;
     608               0 :     mInput->GetCompleteDefaultIndex(&shouldComplete);
     609               0 :     if (shouldComplete) {
     610               0 :       nsAutoString value;
     611               0 :       if (NS_SUCCEEDED(GetResultValueAt(index, true, value))) {
     612               0 :         CompleteValue(value);
     613                 :       }
     614                 :     }
     615                 : 
     616                 :     // Invalidate the popup.
     617               0 :     popup->Invalidate();
     618                 :   } else {
     619                 :     // Nothing left in the popup, clear any pending search timers and
     620                 :     // close the popup.
     621               0 :     ClearSearchTimer();
     622               0 :     ClosePopup();
     623                 :   }
     624                 : 
     625               0 :   return NS_OK;
     626                 : }
     627                 : 
     628                 : nsresult 
     629             966 : nsAutoCompleteController::GetResultAt(PRInt32 aIndex, nsIAutoCompleteResult** aResult,
     630                 :                                       PRInt32* aRowIndex)
     631                 : {
     632                 :   PRInt32 searchIndex;
     633             966 :   RowIndexToSearch(aIndex, &searchIndex, aRowIndex);
     634             966 :   NS_ENSURE_TRUE(searchIndex >= 0 && *aRowIndex >= 0, NS_ERROR_FAILURE);
     635                 : 
     636             966 :   *aResult = mResults[searchIndex];
     637             966 :   NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE);
     638             966 :   return NS_OK;
     639                 : }
     640                 : 
     641                 : NS_IMETHODIMP
     642             531 : nsAutoCompleteController::GetValueAt(PRInt32 aIndex, nsAString & _retval)
     643                 : {
     644             531 :   GetResultLabelAt(aIndex, false, _retval);
     645                 : 
     646             531 :   return NS_OK;
     647                 : }
     648                 : 
     649                 : NS_IMETHODIMP
     650               0 : nsAutoCompleteController::GetLabelAt(PRInt32 aIndex, nsAString & _retval)
     651                 : {
     652               0 :   GetResultLabelAt(aIndex, false, _retval);
     653                 : 
     654               0 :   return NS_OK;
     655                 : }
     656                 : 
     657                 : NS_IMETHODIMP
     658             350 : nsAutoCompleteController::GetCommentAt(PRInt32 aIndex, nsAString & _retval)
     659                 : {
     660                 :   PRInt32 rowIndex;
     661                 :   nsIAutoCompleteResult* result;
     662             350 :   nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
     663             350 :   NS_ENSURE_SUCCESS(rv, rv);
     664                 : 
     665             350 :   return result->GetCommentAt(rowIndex, _retval);
     666                 : }
     667                 : 
     668                 : NS_IMETHODIMP
     669              84 : nsAutoCompleteController::GetStyleAt(PRInt32 aIndex, nsAString & _retval)
     670                 : {
     671                 :   PRInt32 rowIndex;
     672                 :   nsIAutoCompleteResult* result;
     673              84 :   nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
     674              84 :   NS_ENSURE_SUCCESS(rv, rv);
     675                 : 
     676              84 :   return result->GetStyleAt(rowIndex, _retval);
     677                 : }
     678                 : 
     679                 : NS_IMETHODIMP
     680               0 : nsAutoCompleteController::GetImageAt(PRInt32 aIndex, nsAString & _retval)
     681                 : {
     682                 :   PRInt32 rowIndex;
     683                 :   nsIAutoCompleteResult* result;
     684               0 :   nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
     685               0 :   NS_ENSURE_SUCCESS(rv, rv);
     686                 : 
     687               0 :   return result->GetImageAt(rowIndex, _retval);
     688                 : }
     689                 : 
     690                 : NS_IMETHODIMP
     691               0 : nsAutoCompleteController::SetSearchString(const nsAString &aSearchString)
     692                 : {
     693               0 :   mSearchString = aSearchString;
     694               0 :   return NS_OK;
     695                 : }
     696                 : 
     697                 : NS_IMETHODIMP
     698               0 : nsAutoCompleteController::GetSearchString(nsAString &aSearchString)
     699                 : {
     700               0 :   aSearchString = mSearchString;
     701               0 :   return NS_OK;
     702                 : }
     703                 : 
     704                 : 
     705                 : ////////////////////////////////////////////////////////////////////////
     706                 : //// nsIAutoCompleteObserver
     707                 : 
     708                 : NS_IMETHODIMP
     709               0 : nsAutoCompleteController::OnUpdateSearchResult(nsIAutoCompleteSearch *aSearch, nsIAutoCompleteResult* aResult)
     710                 : {
     711               0 :   ClearResults();
     712               0 :   return OnSearchResult(aSearch, aResult);
     713                 : }
     714                 : 
     715                 : NS_IMETHODIMP
     716             429 : nsAutoCompleteController::OnSearchResult(nsIAutoCompleteSearch *aSearch, nsIAutoCompleteResult* aResult)
     717                 : {
     718                 :   // look up the index of the search which is returning
     719             429 :   PRUint32 count = mSearches.Count();
     720             877 :   for (PRUint32 i = 0; i < count; ++i) {
     721             448 :     if (mSearches[i] == aSearch) {
     722             429 :       ProcessResult(i, aResult);
     723                 :     }
     724                 :   }
     725                 : 
     726             429 :   return NS_OK;
     727                 : }
     728                 : 
     729                 : ////////////////////////////////////////////////////////////////////////
     730                 : //// nsITimerCallback
     731                 : 
     732                 : NS_IMETHODIMP
     733             219 : nsAutoCompleteController::Notify(nsITimer *timer)
     734                 : {
     735             219 :   mTimer = nsnull;
     736                 : 
     737             219 :   if (mImmediateSearchesCount == 0) {
     738                 :     // If there were no immediate searches, BeforeSearches has not yet been
     739                 :     // called, so do it now.
     740             218 :     nsresult rv = BeforeSearches();
     741             218 :     if (NS_FAILED(rv))
     742               0 :       return rv;
     743                 :   }
     744             219 :   StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED);
     745             219 :   AfterSearches();
     746             219 :   return NS_OK;
     747                 : }
     748                 : 
     749                 : ////////////////////////////////////////////////////////////////////////
     750                 : // nsITreeView
     751                 : 
     752                 : NS_IMETHODIMP
     753               0 : nsAutoCompleteController::GetRowCount(PRInt32 *aRowCount)
     754                 : {
     755               0 :   *aRowCount = mRowCount;
     756               0 :   return NS_OK;
     757                 : }
     758                 : 
     759                 : NS_IMETHODIMP
     760               0 : nsAutoCompleteController::GetRowProperties(PRInt32 index, nsISupportsArray *properties)
     761                 : {
     762               0 :   return NS_OK;
     763                 : }
     764                 : 
     765                 : NS_IMETHODIMP
     766               0 : nsAutoCompleteController::GetCellProperties(PRInt32 row, nsITreeColumn* col, nsISupportsArray* properties)
     767                 : {
     768               0 :   if (row >= 0) {
     769               0 :     nsAutoString className;
     770               0 :     GetStyleAt(row, className);
     771               0 :     if (!className.IsEmpty()) {
     772               0 :       nsCOMPtr<nsIAtom> atom(do_GetAtom(className));
     773               0 :       properties->AppendElement(atom);
     774                 :     }
     775                 :   }
     776                 : 
     777               0 :   return NS_OK;
     778                 : }
     779                 : 
     780                 : NS_IMETHODIMP
     781               0 : nsAutoCompleteController::GetColumnProperties(nsITreeColumn* col, nsISupportsArray* properties)
     782                 : {
     783               0 :   return NS_OK;
     784                 : }
     785                 : 
     786                 : NS_IMETHODIMP
     787               0 : nsAutoCompleteController::GetImageSrc(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
     788                 : {
     789                 :   const PRUnichar* colID;
     790               0 :   col->GetIdConst(&colID);
     791                 : 
     792               0 :   if (NS_LITERAL_STRING("treecolAutoCompleteValue").Equals(colID))
     793               0 :     return GetImageAt(row, _retval);
     794                 : 
     795               0 :   return NS_OK;
     796                 : }
     797                 : 
     798                 : NS_IMETHODIMP
     799               0 : nsAutoCompleteController::GetProgressMode(PRInt32 row, nsITreeColumn* col, PRInt32* _retval)
     800                 : {
     801               0 :   NS_NOTREACHED("tree has no progress cells");
     802               0 :   return NS_OK;
     803                 : }
     804                 : 
     805                 : NS_IMETHODIMP
     806               0 : nsAutoCompleteController::GetCellValue(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
     807                 : {
     808               0 :   NS_NOTREACHED("all of our cells are text");
     809               0 :   return NS_OK;
     810                 : }
     811                 : 
     812                 : NS_IMETHODIMP
     813               0 : nsAutoCompleteController::GetCellText(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
     814                 : {
     815                 :   const PRUnichar* colID;
     816               0 :   col->GetIdConst(&colID);
     817                 : 
     818               0 :   if (NS_LITERAL_STRING("treecolAutoCompleteValue").Equals(colID))
     819               0 :     GetValueAt(row, _retval);
     820               0 :   else if (NS_LITERAL_STRING("treecolAutoCompleteComment").Equals(colID))
     821               0 :     GetCommentAt(row, _retval);
     822                 : 
     823               0 :   return NS_OK;
     824                 : }
     825                 : 
     826                 : NS_IMETHODIMP
     827               0 : nsAutoCompleteController::IsContainer(PRInt32 index, bool *_retval)
     828                 : {
     829               0 :   *_retval = false;
     830               0 :   return NS_OK;
     831                 : }
     832                 : 
     833                 : NS_IMETHODIMP
     834               0 : nsAutoCompleteController::IsContainerOpen(PRInt32 index, bool *_retval)
     835                 : {
     836               0 :   NS_NOTREACHED("no container cells");
     837               0 :   return NS_OK;
     838                 : }
     839                 : 
     840                 : NS_IMETHODIMP
     841               0 : nsAutoCompleteController::IsContainerEmpty(PRInt32 index, bool *_retval)
     842                 : {
     843               0 :   NS_NOTREACHED("no container cells");
     844               0 :   return NS_OK;
     845                 : }
     846                 : 
     847                 : NS_IMETHODIMP
     848               0 : nsAutoCompleteController::GetLevel(PRInt32 index, PRInt32 *_retval)
     849                 : {
     850               0 :   *_retval = 0;
     851               0 :   return NS_OK;
     852                 : }
     853                 : 
     854                 : NS_IMETHODIMP
     855               0 : nsAutoCompleteController::GetParentIndex(PRInt32 rowIndex, PRInt32 *_retval)
     856                 : {
     857               0 :   *_retval = -1;
     858               0 :   return NS_OK;
     859                 : }
     860                 : 
     861                 : NS_IMETHODIMP
     862               0 : nsAutoCompleteController::HasNextSibling(PRInt32 rowIndex, PRInt32 afterIndex, bool *_retval)
     863                 : {
     864               0 :   *_retval = false;
     865               0 :   return NS_OK;
     866                 : }
     867                 : 
     868                 : NS_IMETHODIMP
     869               0 : nsAutoCompleteController::ToggleOpenState(PRInt32 index)
     870                 : {
     871               0 :   return NS_OK;
     872                 : }
     873                 : 
     874                 : NS_IMETHODIMP
     875               0 : nsAutoCompleteController::SetTree(nsITreeBoxObject *tree)
     876                 : {
     877               0 :   mTree = tree;
     878               0 :   return NS_OK;
     879                 : }
     880                 : 
     881                 : NS_IMETHODIMP
     882               0 : nsAutoCompleteController::GetSelection(nsITreeSelection * *aSelection)
     883                 : {
     884               0 :   *aSelection = mSelection;
     885               0 :   NS_IF_ADDREF(*aSelection);
     886               0 :   return NS_OK;
     887                 : }
     888                 : 
     889               0 : NS_IMETHODIMP nsAutoCompleteController::SetSelection(nsITreeSelection * aSelection)
     890                 : {
     891               0 :   mSelection = aSelection;
     892               0 :   return NS_OK;
     893                 : }
     894                 : 
     895                 : NS_IMETHODIMP
     896               0 : nsAutoCompleteController::SelectionChanged()
     897                 : {
     898               0 :   return NS_OK;
     899                 : }
     900                 : 
     901                 : NS_IMETHODIMP
     902               0 : nsAutoCompleteController::SetCellValue(PRInt32 row, nsITreeColumn* col, const nsAString& value)
     903                 : {
     904               0 :   return NS_OK;
     905                 : }
     906                 : 
     907                 : NS_IMETHODIMP
     908               0 : nsAutoCompleteController::SetCellText(PRInt32 row, nsITreeColumn* col, const nsAString& value)
     909                 : {
     910               0 :   return NS_OK;
     911                 : }
     912                 : 
     913                 : NS_IMETHODIMP
     914               0 : nsAutoCompleteController::CycleHeader(nsITreeColumn* col)
     915                 : {
     916               0 :   return NS_OK;
     917                 : }
     918                 : 
     919                 : NS_IMETHODIMP
     920               0 : nsAutoCompleteController::CycleCell(PRInt32 row, nsITreeColumn* col)
     921                 : {
     922               0 :   return NS_OK;
     923                 : }
     924                 : 
     925                 : NS_IMETHODIMP
     926               0 : nsAutoCompleteController::IsEditable(PRInt32 row, nsITreeColumn* col, bool *_retval)
     927                 : {
     928               0 :   *_retval = false;
     929               0 :   return NS_OK;
     930                 : }
     931                 : 
     932                 : NS_IMETHODIMP
     933               0 : nsAutoCompleteController::IsSelectable(PRInt32 row, nsITreeColumn* col, bool *_retval)
     934                 : {
     935               0 :   *_retval = false;
     936               0 :   return NS_OK;
     937                 : }
     938                 : 
     939                 : NS_IMETHODIMP
     940               0 : nsAutoCompleteController::IsSeparator(PRInt32 index, bool *_retval)
     941                 : {
     942               0 :   *_retval = false;
     943               0 :   return NS_OK;
     944                 : }
     945                 : 
     946                 : NS_IMETHODIMP
     947               0 : nsAutoCompleteController::IsSorted(bool *_retval)
     948                 : {
     949               0 :   *_retval = false;
     950               0 :   return NS_OK;
     951                 : }
     952                 : 
     953                 : NS_IMETHODIMP
     954               0 : nsAutoCompleteController::CanDrop(PRInt32 index, PRInt32 orientation,
     955                 :                                   nsIDOMDataTransfer* dataTransfer, bool *_retval)
     956                 : {
     957               0 :   return NS_OK;
     958                 : }
     959                 : 
     960                 : NS_IMETHODIMP
     961               0 : nsAutoCompleteController::Drop(PRInt32 row, PRInt32 orientation, nsIDOMDataTransfer* dataTransfer)
     962                 : {
     963               0 :   return NS_OK;
     964                 : }
     965                 : 
     966                 : NS_IMETHODIMP
     967               0 : nsAutoCompleteController::PerformAction(const PRUnichar *action)
     968                 : {
     969               0 :   return NS_OK;
     970                 : }
     971                 : 
     972                 : NS_IMETHODIMP
     973               0 : nsAutoCompleteController::PerformActionOnRow(const PRUnichar *action, PRInt32 row)
     974                 : {
     975               0 :   return NS_OK;
     976                 : }
     977                 : 
     978                 : NS_IMETHODIMP
     979               0 : nsAutoCompleteController::PerformActionOnCell(const PRUnichar* action, PRInt32 row, nsITreeColumn* col)
     980                 : {
     981               0 :   return NS_OK;
     982                 : }
     983                 : 
     984                 : ////////////////////////////////////////////////////////////////////////
     985                 : //// nsAutoCompleteController
     986                 : 
     987                 : nsresult
     988             631 : nsAutoCompleteController::OpenPopup()
     989                 : {
     990                 :   PRUint32 minResults;
     991             631 :   mInput->GetMinResultsForPopup(&minResults);
     992                 : 
     993             631 :   if (mRowCount >= minResults) {
     994             631 :     return mInput->SetPopupOpen(true);
     995                 :   }
     996                 : 
     997               0 :   return NS_OK;
     998                 : }
     999                 : 
    1000                 : nsresult
    1001             272 : nsAutoCompleteController::ClosePopup()
    1002                 : {
    1003             272 :   if (!mInput) {
    1004               0 :     return NS_OK;
    1005                 :   }
    1006                 : 
    1007             272 :   bool isOpen = false;
    1008             272 :   mInput->GetPopupOpen(&isOpen);
    1009             272 :   if (!isOpen)
    1010              78 :     return NS_OK;
    1011                 : 
    1012             388 :   nsCOMPtr<nsIAutoCompletePopup> popup;
    1013             194 :   mInput->GetPopup(getter_AddRefs(popup));
    1014             194 :   NS_ENSURE_TRUE(popup != nsnull, NS_ERROR_FAILURE);
    1015             194 :   popup->SetSelectedIndex(-1);
    1016             194 :   return mInput->SetPopupOpen(false);
    1017                 : }
    1018                 : 
    1019                 : nsresult
    1020             248 : nsAutoCompleteController::BeforeSearches()
    1021                 : {
    1022             248 :   NS_ENSURE_STATE(mInput);
    1023                 : 
    1024             248 :   mSearchStatus = nsIAutoCompleteController::STATUS_SEARCHING;
    1025             248 :   mDefaultIndexCompleted = false;
    1026                 : 
    1027                 :   // The first search result will clear mResults array, though we should pass
    1028                 :   // the previous result to each search to allow them to reuse it.  So we
    1029                 :   // temporarily cache current results till AfterSearches().
    1030             248 :   if (!mResultCache.AppendObjects(mResults)) {
    1031               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1032                 :   }
    1033                 : 
    1034             248 :   mSearchesOngoing = mSearches.Count();
    1035             248 :   mSearchesFailed = 0;
    1036             248 :   mFirstSearchResult = true;
    1037                 : 
    1038                 :   // notify the input that the search is beginning
    1039             248 :   mInput->OnSearchBegin();
    1040                 : 
    1041             248 :   return NS_OK;
    1042                 : }
    1043                 : 
    1044                 : nsresult
    1045             278 : nsAutoCompleteController::StartSearch(PRUint16 aSearchType)
    1046                 : {
    1047             278 :   NS_ENSURE_STATE(mInput);
    1048             556 :   nsCOMPtr<nsIAutoCompleteInput> input = mInput;
    1049                 : 
    1050             567 :   for (PRInt32 i = 0; i < mSearches.Count(); ++i) {
    1051             578 :     nsCOMPtr<nsIAutoCompleteSearch> search = mSearches[i];
    1052                 : 
    1053                 :     // Filter on search type.  Not all the searches implement this interface,
    1054                 :     // in such a case just consider them delayed.
    1055             289 :     PRUint16 searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
    1056                 :     nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
    1057             578 :       do_QueryInterface(search);
    1058             289 :     if (searchDesc)
    1059              58 :       searchDesc->GetSearchType(&searchType);
    1060             289 :     if (searchType != aSearchType)
    1061              32 :       continue;
    1062                 : 
    1063             257 :     nsIAutoCompleteResult *result = mResultCache.SafeObjectAt(i);
    1064                 : 
    1065             257 :     if (result) {
    1066                 :       PRUint16 searchResult;
    1067               2 :       result->GetSearchResult(&searchResult);
    1068               2 :       if (searchResult != nsIAutoCompleteResult::RESULT_SUCCESS &&
    1069                 :           searchResult != nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING &&
    1070                 :           searchResult != nsIAutoCompleteResult::RESULT_NOMATCH)
    1071               0 :         result = nsnull;
    1072                 :     }
    1073                 : 
    1074             514 :     nsAutoString searchParam;
    1075             257 :     nsresult rv = input->GetSearchParam(searchParam);
    1076             257 :     if (NS_FAILED(rv))
    1077               0 :         return rv;
    1078                 : 
    1079             257 :     rv = search->StartSearch(mSearchString, searchParam, result, static_cast<nsIAutoCompleteObserver *>(this));
    1080             257 :     if (NS_FAILED(rv)) {
    1081               1 :       ++mSearchesFailed;
    1082               1 :       --mSearchesOngoing;
    1083                 :     }
    1084                 :     // Because of the joy of nested event loops (which can easily happen when some
    1085                 :     // code uses a generator for an asynchronous AutoComplete search),
    1086                 :     // nsIAutoCompleteSearch::StartSearch might cause us to be detached from our input
    1087                 :     // field.  The next time we iterate, we'd be touching something that we shouldn't
    1088                 :     // be, and result in a crash.
    1089             257 :     if (!mInput) {
    1090                 :       // The search operation has been finished.
    1091               0 :       return NS_OK;
    1092                 :     }
    1093                 :   }
    1094                 : 
    1095             278 :   return NS_OK;
    1096                 : }
    1097                 : 
    1098                 : void
    1099             248 : nsAutoCompleteController::AfterSearches()
    1100                 : {
    1101             248 :   mResultCache.Clear();
    1102             248 :   if (mSearchesFailed == mSearches.Count())
    1103               0 :     PostSearchCleanup();
    1104             248 : }
    1105                 : 
    1106                 : NS_IMETHODIMP
    1107             258 : nsAutoCompleteController::StopSearch()
    1108                 : {
    1109                 :   // Stop the timer if there is one
    1110             258 :   ClearSearchTimer();
    1111                 : 
    1112                 :   // Stop any ongoing asynchronous searches
    1113             258 :   if (mSearchStatus == nsIAutoCompleteController::STATUS_SEARCHING) {
    1114               7 :     PRUint32 count = mSearches.Count();
    1115                 : 
    1116              14 :     for (PRUint32 i = 0; i < count; ++i) {
    1117              14 :       nsCOMPtr<nsIAutoCompleteSearch> search = mSearches[i];
    1118               7 :       search->StopSearch();
    1119                 :     }
    1120               7 :     mSearchesOngoing = 0;
    1121                 :     // since we were searching, but now we've stopped,
    1122                 :     // we need to call PostSearchCleanup()
    1123               7 :     PostSearchCleanup();
    1124                 :   }
    1125             258 :   return NS_OK;
    1126                 : }
    1127                 : 
    1128                 : nsresult
    1129             250 : nsAutoCompleteController::StartSearches()
    1130                 : {
    1131                 :   // Don't create a new search timer if we're already waiting for one to fire.
    1132                 :   // If we don't check for this, we won't be able to cancel the original timer
    1133                 :   // and may crash when it fires (bug 236659).
    1134             250 :   if (mTimer || !mInput)
    1135               2 :     return NS_OK;
    1136                 : 
    1137                 :   // Get the timeout for delayed searches.
    1138                 :   PRUint32 timeout;
    1139             248 :   mInput->GetTimeout(&timeout);
    1140                 : 
    1141             248 :   PRUint32 immediateSearchesCount = mImmediateSearchesCount;
    1142             248 :   if (timeout == 0) {
    1143                 :     // All the searches should be executed immediately.
    1144               2 :     immediateSearchesCount = mSearches.Count();
    1145                 :   }
    1146                 : 
    1147             248 :   if (immediateSearchesCount > 0) {
    1148              30 :     nsresult rv = BeforeSearches();
    1149              30 :     if (NS_FAILED(rv))
    1150               0 :       return rv;
    1151              30 :     StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE);
    1152                 : 
    1153              30 :     if (mSearches.Count() == immediateSearchesCount) {
    1154                 :       // Either all searches are immediate, or the timeout is 0.  In the
    1155                 :       // latter case we still have to execute the delayed searches, otherwise
    1156                 :       // this will be a no-op.
    1157              29 :       StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED);
    1158                 : 
    1159                 :       // All the searches have been started, just finish.
    1160              29 :       AfterSearches();
    1161              29 :       return NS_OK;
    1162                 :     }
    1163                 :   }
    1164                 : 
    1165             219 :   MOZ_ASSERT(timeout > 0, "Trying to delay searches with a 0 timeout!");
    1166                 : 
    1167                 :   // Now start the delayed searches.
    1168                 :   nsresult rv;
    1169             219 :   mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1170             219 :   if (NS_FAILED(rv))
    1171               0 :       return rv;
    1172             219 :   rv = mTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
    1173             219 :   if (NS_FAILED(rv))
    1174               0 :       mTimer = nsnull;
    1175                 : 
    1176             219 :   return rv;
    1177                 : }
    1178                 : 
    1179                 : nsresult
    1180             259 : nsAutoCompleteController::ClearSearchTimer()
    1181                 : {
    1182             259 :   if (mTimer) {
    1183               0 :     mTimer->Cancel();
    1184               0 :     mTimer = nsnull;
    1185                 :   }
    1186             259 :   return NS_OK;
    1187                 : }
    1188                 : 
    1189                 : nsresult
    1190              11 : nsAutoCompleteController::EnterMatch(bool aIsPopupSelection)
    1191                 : {
    1192              22 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
    1193              22 :   nsCOMPtr<nsIAutoCompletePopup> popup;
    1194              11 :   input->GetPopup(getter_AddRefs(popup));
    1195              11 :   NS_ENSURE_TRUE(popup != nsnull, NS_ERROR_FAILURE);
    1196                 : 
    1197                 :   bool forceComplete;
    1198              11 :   input->GetForceComplete(&forceComplete);
    1199                 : 
    1200                 :   // Ask the popup if it wants to enter a special value into the textbox
    1201              22 :   nsAutoString value;
    1202              11 :   popup->GetOverrideValue(value);
    1203              11 :   if (value.IsEmpty()) {
    1204                 :     bool shouldComplete;
    1205              11 :     mInput->GetCompleteDefaultIndex(&shouldComplete);
    1206                 :     bool completeSelection;
    1207              11 :     input->GetCompleteSelectedIndex(&completeSelection);
    1208                 : 
    1209                 :     // If completeselectedindex is false or a row was selected from the popup,
    1210                 :     // enter it into the textbox. If completeselectedindex is true, or
    1211                 :     // EnterMatch was called via other means, for instance pressing Enter,
    1212                 :     // don't fill in the value as it will have already been filled in as needed.
    1213                 :     PRInt32 selectedIndex;
    1214              11 :     popup->GetSelectedIndex(&selectedIndex);
    1215              11 :     if (selectedIndex >= 0 && (!completeSelection || aIsPopupSelection))
    1216               1 :       GetResultValueAt(selectedIndex, true, value);
    1217              10 :     else if (shouldComplete) {
    1218                 :       // We usually try to preserve the casing of what user has typed, but
    1219                 :       // if he wants to autocomplete, we will replace the value with the
    1220                 :       // actual autocomplete result.
    1221                 :       // The user wants explicitely to use that result, so this ensures
    1222                 :       // association of the result with the autocompleted text.
    1223              16 :       nsAutoString defaultIndexValue;
    1224              16 :       nsAutoString inputValue;
    1225               8 :       input->GetTextValue(inputValue);
    1226              16 :       if (NS_SUCCEEDED(GetDefaultCompleteValue(-1, false, defaultIndexValue)) &&
    1227               8 :           defaultIndexValue.Equals(inputValue, nsCaseInsensitiveStringComparator()))
    1228               8 :         value = defaultIndexValue;
    1229                 :     }
    1230                 : 
    1231              11 :     if (forceComplete && value.IsEmpty()) {
    1232                 :       // Since nothing was selected, and forceComplete is specified, that means
    1233                 :       // we have to find the first default match and enter it instead
    1234               0 :       PRUint32 count = mResults.Count();
    1235               0 :       for (PRUint32 i = 0; i < count; ++i) {
    1236               0 :         nsIAutoCompleteResult *result = mResults[i];
    1237                 : 
    1238               0 :         if (result) {
    1239                 :           PRInt32 defaultIndex;
    1240               0 :           result->GetDefaultIndex(&defaultIndex);
    1241               0 :           if (defaultIndex >= 0) {
    1242               0 :             result->GetValueAt(defaultIndex, value);
    1243               0 :             break;
    1244                 :           }
    1245                 :         }
    1246                 :       }
    1247                 :     }
    1248                 :   }
    1249                 : 
    1250                 :   nsCOMPtr<nsIObserverService> obsSvc =
    1251              22 :     mozilla::services::GetObserverService();
    1252              11 :   NS_ENSURE_STATE(obsSvc);
    1253              11 :   obsSvc->NotifyObservers(input, "autocomplete-will-enter-text", nsnull);
    1254                 : 
    1255              11 :   if (!value.IsEmpty()) {
    1256               9 :     input->SetTextValue(value);
    1257               9 :     input->SelectTextRange(value.Length(), value.Length());
    1258               9 :     mSearchString = value;
    1259                 :   }
    1260                 : 
    1261              11 :   obsSvc->NotifyObservers(input, "autocomplete-did-enter-text", nsnull);
    1262              11 :   ClosePopup();
    1263                 : 
    1264                 :   bool cancel;
    1265              11 :   input->OnTextEntered(&cancel);
    1266                 : 
    1267              11 :   return NS_OK;
    1268                 : }
    1269                 : 
    1270                 : nsresult
    1271               1 : nsAutoCompleteController::RevertTextValue()
    1272                 : {
    1273                 :   // StopSearch() can call PostSearchCleanup() which might result
    1274                 :   // in a blur event, which could null out mInput, so we need to check it
    1275                 :   // again.  See bug #408463 for more details
    1276               1 :   if (!mInput)
    1277               0 :     return NS_OK;
    1278                 : 
    1279               2 :   nsAutoString oldValue(mSearchString);
    1280               2 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
    1281                 : 
    1282               1 :   bool cancel = false;
    1283               1 :   input->OnTextReverted(&cancel);
    1284                 : 
    1285               1 :   if (!cancel) {
    1286                 :     nsCOMPtr<nsIObserverService> obsSvc =
    1287               2 :       mozilla::services::GetObserverService();
    1288               1 :     NS_ENSURE_STATE(obsSvc);
    1289               1 :     obsSvc->NotifyObservers(input, "autocomplete-will-revert-text", nsnull);
    1290                 : 
    1291               3 :     nsAutoString inputValue;
    1292               1 :     input->GetTextValue(inputValue);
    1293                 :     // Don't change the value if it is the same to prevent sending useless events.
    1294                 :     // NOTE: how can |RevertTextValue| be called with inputValue != oldValue?
    1295               1 :     if (!oldValue.Equals(inputValue)) {
    1296               1 :       input->SetTextValue(oldValue);
    1297                 :     }
    1298                 : 
    1299               1 :     obsSvc->NotifyObservers(input, "autocomplete-did-revert-text", nsnull);
    1300                 :   }
    1301                 : 
    1302               1 :   return NS_OK;
    1303                 : }
    1304                 : 
    1305                 : nsresult
    1306             429 : nsAutoCompleteController::ProcessResult(PRInt32 aSearchIndex, nsIAutoCompleteResult *aResult)
    1307                 : {
    1308             429 :   NS_ENSURE_STATE(mInput);
    1309             858 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
    1310                 : 
    1311                 :   // If this is the first search result we are processing
    1312                 :   // we should clear out the previously cached results
    1313             429 :   if (mFirstSearchResult) {
    1314             241 :     ClearResults();
    1315             241 :     mFirstSearchResult = false;
    1316                 :   }
    1317                 : 
    1318             429 :   PRUint16 result = 0;
    1319             429 :   if (aResult)
    1320             428 :     aResult->GetSearchResult(&result);
    1321                 : 
    1322                 :   // if our results are incremental, the search is still ongoing
    1323             429 :   if (result != nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING &&
    1324                 :       result != nsIAutoCompleteResult::RESULT_NOMATCH_ONGOING) {
    1325             249 :     --mSearchesOngoing;
    1326                 :   }
    1327                 : 
    1328             429 :   PRUint32 oldMatchCount = 0;
    1329             429 :   PRUint32 matchCount = 0;
    1330             429 :   if (aResult)
    1331             428 :     aResult->GetMatchCount(&matchCount);
    1332                 : 
    1333             429 :   PRInt32 resultIndex = mResults.IndexOf(aResult);
    1334             429 :   if (resultIndex == -1) {
    1335                 :     // cache the result
    1336             249 :     mResults.AppendObject(aResult);
    1337             249 :     mMatchCounts.AppendElement(matchCount);
    1338             249 :     resultIndex = mResults.Count() - 1;
    1339                 :   }
    1340                 :   else {
    1341             180 :     oldMatchCount = mMatchCounts[aSearchIndex];
    1342             180 :     mMatchCounts[resultIndex] = matchCount;
    1343                 :   }
    1344                 : 
    1345             429 :   bool isTypeAheadResult = false;
    1346             429 :   if (aResult) {
    1347             428 :     aResult->GetTypeAheadResult(&isTypeAheadResult);
    1348                 :   }
    1349                 : 
    1350             429 :   if (!isTypeAheadResult) {
    1351             401 :     PRUint32 oldRowCount = mRowCount;
    1352                 :     // If the search failed, increase the match count to include the error
    1353                 :     // description.
    1354             401 :     if (result == nsIAutoCompleteResult::RESULT_FAILURE) {
    1355               0 :       nsAutoString error;
    1356               0 :       aResult->GetErrorDescription(error);
    1357               0 :       if (!error.IsEmpty()) {
    1358               0 :         ++mRowCount;
    1359               0 :         if (mTree) {
    1360               0 :           mTree->RowCountChanged(oldRowCount, 1);
    1361                 :         }
    1362                 :       }
    1363             401 :     } else if (result == nsIAutoCompleteResult::RESULT_SUCCESS ||
    1364                 :                result == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) {
    1365                 :       // Increase the match count for all matches in this result.
    1366             384 :       mRowCount += matchCount - oldMatchCount;
    1367                 : 
    1368             384 :       if (mTree) {
    1369               0 :         mTree->RowCountChanged(oldRowCount, matchCount - oldMatchCount);
    1370                 :       }
    1371                 :     }
    1372                 : 
    1373                 :     // Refresh the popup view to display the new search results
    1374             802 :     nsCOMPtr<nsIAutoCompletePopup> popup;
    1375             401 :     input->GetPopup(getter_AddRefs(popup));
    1376             401 :     NS_ENSURE_TRUE(popup != nsnull, NS_ERROR_FAILURE);
    1377             401 :     popup->Invalidate();
    1378                 : 
    1379                 :     // Make sure the popup is open, if necessary, since we now have at least one
    1380                 :     // search result ready to display. Don't force the popup closed if we might
    1381                 :     // get results in the future to avoid unnecessarily canceling searches.
    1382             401 :     if (mRowCount) {
    1383             383 :       OpenPopup();
    1384              18 :     } else if (result != nsIAutoCompleteResult::RESULT_NOMATCH_ONGOING) {
    1385              18 :       ClosePopup();
    1386                 :     }
    1387                 :   }
    1388                 : 
    1389             429 :   if (result == nsIAutoCompleteResult::RESULT_SUCCESS ||
    1390                 :       result == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) {
    1391                 :     // Try to autocomplete the default index for this search.
    1392             407 :     CompleteDefaultIndex(resultIndex);
    1393                 :   }
    1394                 : 
    1395             429 :   if (mSearchesOngoing == 0) {
    1396                 :     // If this is the last search to return, cleanup.
    1397             241 :     PostSearchCleanup();
    1398                 :   }
    1399                 : 
    1400             429 :   return NS_OK;
    1401                 : }
    1402                 : 
    1403                 : nsresult
    1404             248 : nsAutoCompleteController::PostSearchCleanup()
    1405                 : {
    1406             248 :   NS_ENSURE_STATE(mInput);
    1407             496 :   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
    1408                 : 
    1409                 :   PRUint32 minResults;
    1410             248 :   mInput->GetMinResultsForPopup(&minResults);
    1411                 : 
    1412             248 :   if (mRowCount || minResults == 0) {
    1413             248 :     OpenPopup();
    1414             496 :     if (mRowCount)
    1415             198 :       mSearchStatus = nsIAutoCompleteController::STATUS_COMPLETE_MATCH;
    1416                 :     else
    1417              50 :       mSearchStatus = nsIAutoCompleteController::STATUS_COMPLETE_NO_MATCH;
    1418                 :   } else {
    1419               0 :     mSearchStatus = nsIAutoCompleteController::STATUS_COMPLETE_NO_MATCH;
    1420               0 :     ClosePopup();
    1421                 :   }
    1422                 : 
    1423                 :   // notify the input that the search is complete
    1424             248 :   input->OnSearchComplete();
    1425                 : 
    1426             248 :   return NS_OK;
    1427                 : }
    1428                 : 
    1429                 : nsresult
    1430             485 : nsAutoCompleteController::ClearResults()
    1431                 : {
    1432             485 :   PRInt32 oldRowCount = mRowCount;
    1433             485 :   mRowCount = 0;
    1434             485 :   mResults.Clear();
    1435             485 :   mMatchCounts.Clear();
    1436             485 :   if (oldRowCount != 0) {
    1437             198 :     if (mTree)
    1438               0 :       mTree->RowCountChanged(0, -oldRowCount);
    1439             198 :     else if (mInput) {
    1440             396 :       nsCOMPtr<nsIAutoCompletePopup> popup;
    1441             198 :       mInput->GetPopup(getter_AddRefs(popup));
    1442             198 :       NS_ENSURE_TRUE(popup != nsnull, NS_ERROR_FAILURE);
    1443                 :       // if we had a tree, RowCountChanged() would have cleared the selection
    1444                 :       // when the selected row was removed.  But since we don't have a tree,
    1445                 :       // we need to clear the selection manually.
    1446             368 :       popup->SetSelectedIndex(-1);
    1447                 :     }
    1448                 :   }
    1449             457 :   return NS_OK;
    1450                 : }
    1451                 : 
    1452                 : nsresult
    1453             407 : nsAutoCompleteController::CompleteDefaultIndex(PRInt32 aResultIndex)
    1454                 : {
    1455             407 :   if (mDefaultIndexCompleted || mBackspaced || mSearchString.Length() == 0 || !mInput)
    1456              31 :     return NS_OK;
    1457                 : 
    1458                 :   PRInt32 selectionStart;
    1459             376 :   mInput->GetSelectionStart(&selectionStart);
    1460                 :   PRInt32 selectionEnd;
    1461             376 :   mInput->GetSelectionEnd(&selectionEnd);
    1462                 : 
    1463                 :   // Don't try to automatically complete to the first result if there's already
    1464                 :   // a selection or the cursor isn't at the end of the input
    1465             752 :   if (selectionEnd != selectionStart ||
    1466             376 :       selectionEnd != (PRInt32)mSearchString.Length())
    1467             345 :     return NS_OK;
    1468                 : 
    1469                 :   bool shouldComplete;
    1470              31 :   mInput->GetCompleteDefaultIndex(&shouldComplete);
    1471              31 :   if (!shouldComplete)
    1472               0 :     return NS_OK;
    1473                 : 
    1474              62 :   nsAutoString resultValue;
    1475              31 :   if (NS_SUCCEEDED(GetDefaultCompleteValue(aResultIndex, true, resultValue)))
    1476              29 :     CompleteValue(resultValue);
    1477                 : 
    1478              31 :   mDefaultIndexCompleted = true;
    1479                 : 
    1480              31 :   return NS_OK;
    1481                 : }
    1482                 : 
    1483                 : nsresult
    1484              40 : nsAutoCompleteController::GetDefaultCompleteValue(PRInt32 aResultIndex,
    1485                 :                                                   bool aPreserveCasing,
    1486                 :                                                   nsAString &_retval)
    1487                 : {
    1488              40 :   PRInt32 defaultIndex = -1;
    1489              40 :   PRInt32 index = aResultIndex;
    1490              40 :   if (index < 0) {
    1491               9 :     PRUint32 count = mResults.Count();
    1492               9 :     for (PRUint32 i = 0; i < count; ++i) {
    1493               9 :       nsIAutoCompleteResult *result = mResults[i];
    1494               9 :       if (result && NS_SUCCEEDED(result->GetDefaultIndex(&defaultIndex)) &&
    1495                 :           defaultIndex >= 0) {
    1496               9 :         index = i;
    1497               9 :         break;
    1498                 :       }
    1499                 :     }
    1500                 :   }
    1501              40 :   NS_ENSURE_TRUE(index >= 0, NS_ERROR_FAILURE);
    1502                 : 
    1503              40 :   nsIAutoCompleteResult *result = mResults.SafeObjectAt(index);
    1504              40 :   NS_ENSURE_TRUE(result != nsnull, NS_ERROR_FAILURE);
    1505                 : 
    1506              40 :   if (defaultIndex < 0) {
    1507                 :     // The search must explicitly provide a default index in order
    1508                 :     // for us to be able to complete.
    1509              31 :     result->GetDefaultIndex(&defaultIndex);
    1510                 :   }
    1511              40 :   if (defaultIndex < 0) {
    1512                 :     // We were given a result index, but that result doesn't want to
    1513                 :     // be autocompleted.
    1514               0 :     return NS_ERROR_FAILURE;
    1515                 :   }
    1516                 : 
    1517                 :   // If the result wrongly notifies a RESULT_SUCCESS with no matches, or
    1518                 :   // provides a defaultIndex greater than its matchCount, avoid trying to
    1519                 :   // complete to an empty value.
    1520              40 :   PRUint32 matchCount = 0;
    1521              40 :   result->GetMatchCount(&matchCount);
    1522                 :   // Here defaultIndex is surely non-negative, so can be cast to unsigned.
    1523              40 :   if ((PRUint32)defaultIndex >= matchCount)
    1524               2 :     return NS_ERROR_FAILURE;
    1525                 : 
    1526              76 :   nsAutoString resultValue;
    1527              38 :   result->GetValueAt(defaultIndex, resultValue);
    1528              67 :   if (aPreserveCasing &&
    1529                 :       StringBeginsWith(resultValue, mSearchString,
    1530              29 :                        nsCaseInsensitiveStringComparator())) {
    1531                 :     // We try to preserve user casing, otherwise we would end up changing
    1532                 :     // the case of what he typed, if we have a result with a different casing.
    1533                 :     // For example if we have result "Test", and user starts writing "tuna",
    1534                 :     // after digiting t, we would convert it to T trying to autocomplete "Test".
    1535                 :     // We will still complete to cased "Test" if the user explicitely choose
    1536                 :     // that result, by either selecting it in the results popup, or with
    1537                 :     // keyboard navigation or if autocompleting in the middle.
    1538              58 :     nsAutoString casedResultValue;
    1539              29 :     casedResultValue.Assign(mSearchString);
    1540                 :     // Use what the user has typed so far.
    1541                 :     casedResultValue.Append(Substring(resultValue,
    1542                 :                                       mSearchString.Length(),
    1543              29 :                                       resultValue.Length()));
    1544              29 :     _retval = casedResultValue;
    1545                 :   }
    1546                 :   else
    1547               9 :     _retval = resultValue;
    1548                 : 
    1549              38 :   return NS_OK;
    1550                 : }
    1551                 : 
    1552                 : nsresult
    1553              29 : nsAutoCompleteController::CompleteValue(nsString &aValue)
    1554                 : /* mInput contains mSearchString, which we want to autocomplete to aValue.  If
    1555                 :  * selectDifference is true, select the remaining portion of aValue not
    1556                 :  * contained in mSearchString. */
    1557                 : {
    1558              29 :   const PRInt32 mSearchStringLength = mSearchString.Length();
    1559              29 :   PRInt32 endSelect = aValue.Length();  // By default, select all of aValue.
    1560                 : 
    1561              58 :   if (aValue.IsEmpty() ||
    1562                 :       StringBeginsWith(aValue, mSearchString,
    1563              29 :                        nsCaseInsensitiveStringComparator())) {
    1564                 :     // aValue is empty (we were asked to clear mInput), or mSearchString
    1565                 :     // matches the beginning of aValue.  In either case we can simply
    1566                 :     // autocomplete to aValue.
    1567              29 :     mInput->SetTextValue(aValue);
    1568                 :   } else {
    1569                 :     nsresult rv;
    1570               0 :     nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
    1571               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1572               0 :     nsCAutoString scheme;
    1573               0 :     if (NS_SUCCEEDED(ios->ExtractScheme(NS_ConvertUTF16toUTF8(aValue), scheme))) {
    1574                 :       // Trying to autocomplete a URI from somewhere other than the beginning.
    1575                 :       // Only succeed if the missing portion is "http://"; otherwise do not
    1576                 :       // autocomplete.  This prevents us from "helpfully" autocompleting to a
    1577                 :       // URI that isn't equivalent to what the user expected.
    1578               0 :       const PRInt32 findIndex = 7; // length of "http://"
    1579                 : 
    1580               0 :       if ((endSelect < findIndex + mSearchStringLength) ||
    1581               0 :           !scheme.LowerCaseEqualsLiteral("http") ||
    1582               0 :           !Substring(aValue, findIndex, mSearchStringLength).Equals(
    1583               0 :             mSearchString, nsCaseInsensitiveStringComparator())) {
    1584               0 :         return NS_OK;
    1585                 :       }
    1586                 : 
    1587               0 :       mInput->SetTextValue(mSearchString +
    1588                 :                            Substring(aValue, mSearchStringLength + findIndex,
    1589               0 :                                      endSelect));
    1590                 : 
    1591               0 :       endSelect -= findIndex; // We're skipping this many characters of aValue.
    1592                 :     } else {
    1593                 :       // Autocompleting something other than a URI from the middle.
    1594                 :       // Use the format "searchstring >> full string" to indicate to the user
    1595                 :       // what we are going to replace their search string with.
    1596               0 :       mInput->SetTextValue(mSearchString + NS_LITERAL_STRING(" >> ") + aValue);
    1597                 : 
    1598               0 :       endSelect = mSearchString.Length() + 4 + aValue.Length();
    1599                 :     }
    1600                 :   }
    1601                 : 
    1602              29 :   mInput->SelectTextRange(mSearchStringLength, endSelect);
    1603                 : 
    1604              29 :   return NS_OK;
    1605                 : }
    1606                 : 
    1607                 : nsresult
    1608             531 : nsAutoCompleteController::GetResultLabelAt(PRInt32 aIndex, bool aValueOnly, nsAString & _retval)
    1609                 : {
    1610             531 :   return GetResultValueLabelAt(aIndex, aValueOnly, false, _retval);
    1611                 : }
    1612                 : 
    1613                 : nsresult
    1614               1 : nsAutoCompleteController::GetResultValueAt(PRInt32 aIndex, bool aValueOnly, nsAString & _retval)
    1615                 : {
    1616               1 :   return GetResultValueLabelAt(aIndex, aValueOnly, true, _retval);
    1617                 : }
    1618                 : 
    1619                 : nsresult
    1620             532 : nsAutoCompleteController::GetResultValueLabelAt(PRInt32 aIndex, bool aValueOnly,
    1621                 :                                                 bool aGetValue, nsAString & _retval)
    1622                 : {
    1623             532 :   NS_ENSURE_TRUE(aIndex >= 0 && (PRUint32) aIndex < mRowCount, NS_ERROR_ILLEGAL_VALUE);
    1624                 : 
    1625                 :   PRInt32 rowIndex;
    1626                 :   nsIAutoCompleteResult *result;
    1627             532 :   nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
    1628             532 :   NS_ENSURE_SUCCESS(rv, rv);
    1629                 : 
    1630                 :   PRUint16 searchResult;
    1631             532 :   result->GetSearchResult(&searchResult);
    1632                 : 
    1633             532 :   if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) {
    1634               0 :     if (aValueOnly)
    1635               0 :       return NS_ERROR_FAILURE;
    1636               0 :     result->GetErrorDescription(_retval);
    1637             532 :   } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS ||
    1638                 :              searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) {
    1639             532 :     if (aGetValue)
    1640               1 :       result->GetValueAt(rowIndex, _retval);
    1641                 :     else
    1642             531 :       result->GetLabelAt(rowIndex, _retval);
    1643                 :   }
    1644                 : 
    1645             532 :   return NS_OK;
    1646                 : }
    1647                 : 
    1648                 : /**
    1649                 :  * Given the index of a row in the autocomplete popup, find the
    1650                 :  * corresponding nsIAutoCompleteSearch index, and sub-index into
    1651                 :  * the search's results list.
    1652                 :  */
    1653                 : nsresult
    1654             966 : nsAutoCompleteController::RowIndexToSearch(PRInt32 aRowIndex, PRInt32 *aSearchIndex, PRInt32 *aItemIndex)
    1655                 : {
    1656             966 :   *aSearchIndex = -1;
    1657             966 :   *aItemIndex = -1;
    1658                 : 
    1659             966 :   PRUint32 count = mSearches.Count();
    1660             966 :   PRUint32 index = 0;
    1661                 : 
    1662                 :   // Move index through the results of each registered nsIAutoCompleteSearch
    1663                 :   // until we find the given row
    1664             971 :   for (PRUint32 i = 0; i < count; ++i) {
    1665             971 :     nsIAutoCompleteResult *result = mResults.SafeObjectAt(i);
    1666             971 :     if (!result)
    1667               0 :       continue;
    1668                 : 
    1669             971 :     PRUint32 rowCount = 0;
    1670                 : 
    1671                 :     // Skip past the result completely if it is marked as hidden
    1672             971 :     bool isTypeAheadResult = false;
    1673             971 :     result->GetTypeAheadResult(&isTypeAheadResult);
    1674                 : 
    1675             971 :     if (!isTypeAheadResult) {
    1676                 :       PRUint16 searchResult;
    1677             970 :       result->GetSearchResult(&searchResult);
    1678                 : 
    1679                 :       // Find out how many results were provided by the
    1680                 :       // current nsIAutoCompleteSearch.
    1681             970 :       if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS ||
    1682                 :           searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) {
    1683             966 :         result->GetMatchCount(&rowCount);
    1684                 :       }
    1685                 :     }
    1686                 : 
    1687                 :     // If the given row index is within the results range
    1688                 :     // of the current nsIAutoCompleteSearch then return the
    1689                 :     // search index and sub-index into the results array
    1690             971 :     if ((rowCount != 0) && (index + rowCount-1 >= (PRUint32) aRowIndex)) {
    1691             966 :       *aSearchIndex = i;
    1692             966 :       *aItemIndex = aRowIndex - index;
    1693             966 :       return NS_OK;
    1694                 :     }
    1695                 : 
    1696                 :     // Advance the popup table index cursor past the
    1697                 :     // results of the current search.
    1698               5 :     index += rowCount;
    1699                 :   }
    1700                 : 
    1701               0 :   return NS_OK;
    1702                 : }
    1703                 : 
    1704              82 : NS_GENERIC_FACTORY_CONSTRUCTOR(nsAutoCompleteController)
    1705             436 : NS_GENERIC_FACTORY_CONSTRUCTOR(nsAutoCompleteSimpleResult)
    1706                 : 
    1707                 : NS_DEFINE_NAMED_CID(NS_AUTOCOMPLETECONTROLLER_CID);
    1708                 : NS_DEFINE_NAMED_CID(NS_AUTOCOMPLETESIMPLERESULT_CID);
    1709                 : 
    1710                 : static const mozilla::Module::CIDEntry kAutoCompleteCIDs[] = {
    1711                 :   { &kNS_AUTOCOMPLETECONTROLLER_CID, false, NULL, nsAutoCompleteControllerConstructor },
    1712                 :   { &kNS_AUTOCOMPLETESIMPLERESULT_CID, false, NULL, nsAutoCompleteSimpleResultConstructor },
    1713                 :   { NULL }
    1714                 : };
    1715                 : 
    1716                 : static const mozilla::Module::ContractIDEntry kAutoCompleteContracts[] = {
    1717                 :   { NS_AUTOCOMPLETECONTROLLER_CONTRACTID, &kNS_AUTOCOMPLETECONTROLLER_CID },
    1718                 :   { NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID, &kNS_AUTOCOMPLETESIMPLERESULT_CID },
    1719                 :   { NULL }
    1720                 : };
    1721                 : 
    1722                 : static const mozilla::Module kAutoCompleteModule = {
    1723                 :   mozilla::Module::kVersion,
    1724                 :   kAutoCompleteCIDs,
    1725                 :   kAutoCompleteContracts
    1726                 : };
    1727                 : 
    1728            4392 : NSMODULE_DEFN(tkAutoCompleteModule) = &kAutoCompleteModule;

Generated by: LCOV version 1.7