Back to index

lightning-sunbird  0.9+nobinonly
nsFormFillController.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Joe Hewitt <hewitt@netscape.com> (Original Author)
00024  *   Dean Tessman <dean_tessman@hotmail.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsFormFillController.h"
00041 
00042 #ifdef MOZ_PLACES
00043 #include "nsStorageFormHistory.h"
00044 #include "nsIAutoCompleteSimpleResult.h"
00045 #else
00046 #include "nsFormHistory.h"
00047 #include "nsIAutoCompleteResultTypes.h"
00048 #endif
00049 #include "nsString.h"
00050 #include "nsReadableUtils.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsIInterfaceRequestor.h"
00053 #include "nsIInterfaceRequestorUtils.h"
00054 #include "nsIDocShellTreeItem.h"
00055 #include "nsIChromeEventHandler.h"
00056 #include "nsPIDOMWindow.h"
00057 #include "nsIWebNavigation.h"
00058 #include "nsIContentViewer.h"
00059 #include "nsIDOMEventTarget.h"
00060 #include "nsIDOMKeyEvent.h"
00061 #include "nsIPrivateDOMEvent.h"
00062 #include "nsIDOMCompositionListener.h"
00063 #include "nsIDOMDocument.h"
00064 #include "nsIDOMElement.h"
00065 #include "nsIDOMNSHTMLInputElement.h"
00066 #include "nsIDocument.h"
00067 #include "nsIScriptGlobalObject.h"
00068 #include "nsIContent.h"
00069 #include "nsIPresShell.h"
00070 #include "nsPresContext.h"
00071 #include "nsIView.h"
00072 #include "nsIFrame.h"
00073 #include "nsIWidget.h"
00074 #include "nsRect.h"
00075 #include "nsIDOMDocumentEvent.h"
00076 #include "nsIDOMHTMLFormElement.h"
00077 #include "nsPasswordManager.h"
00078 #include "nsIDOMMouseEvent.h"
00079 
00080 NS_INTERFACE_MAP_BEGIN(nsFormFillController)
00081   NS_INTERFACE_MAP_ENTRY(nsIFormFillController)
00082   NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteInput)
00083   NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteInput_MOZILLA_1_8_BRANCH)
00084   NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteSearch)
00085   NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
00086   NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
00087   NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
00088   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
00089   NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener)
00090   NS_INTERFACE_MAP_ENTRY(nsIDOMCompositionListener)
00091   NS_INTERFACE_MAP_ENTRY(nsIDOMContextMenuListener)
00092   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFormFillController)
00093   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFocusListener)
00094 NS_INTERFACE_MAP_END
00095 
00096 NS_IMPL_ADDREF(nsFormFillController)
00097 NS_IMPL_RELEASE(nsFormFillController)
00098 
00099 nsFormFillController::nsFormFillController() :
00100   mTimeout(50),
00101   mMinResultsForPopup(1),
00102   mMaxRows(0),
00103   mDisableAutoComplete(PR_FALSE), 
00104   mCompleteDefaultIndex(PR_FALSE),
00105   mCompleteSelectedIndex(PR_FALSE),
00106   mForceComplete(PR_FALSE),
00107   mSuppressOnInput(PR_FALSE)
00108 {
00109   mController = do_CreateInstance("@mozilla.org/autocomplete/controller;1");
00110 
00111   mDocShells = do_CreateInstance("@mozilla.org/supports-array;1");
00112   mPopups = do_CreateInstance("@mozilla.org/supports-array;1");
00113 }
00114 
00115 nsFormFillController::~nsFormFillController()
00116 {
00117   // Remove ourselves as a focus listener from all cached docShells
00118   PRUint32 count;
00119   mDocShells->Count(&count);
00120   for (PRUint32 i = 0; i < count; ++i) {
00121     nsCOMPtr<nsIDocShell> docShell;
00122     mDocShells->GetElementAt(i, getter_AddRefs(docShell));
00123     nsCOMPtr<nsIDOMWindow> domWindow = GetWindowForDocShell(docShell);
00124     RemoveWindowListeners(domWindow);
00125   }
00126 }
00127 
00129 
00130 nsRect
00131 GetScreenOrigin(nsIDOMElement* aElement)
00132 {
00133   nsRect rect(0,0,0,0);
00134   nsSize size;
00135  
00136   nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
00137   nsCOMPtr<nsIDocument> doc = content->GetDocument();
00138 
00139   if (doc) {
00140     // Get Presentation shell 0
00141     nsIPresShell* presShell = doc->GetShellAt(0);
00142     
00143     if (presShell) {
00144       nsPresContext* presContext = presShell->GetPresContext();
00145 
00146       if (presContext) {
00147         // Get the scale from that Presentation Context
00148         float scale;
00149         scale = presContext->TwipsToPixels();
00150 
00151         nsIFrame* frame;
00152         presShell->GetPrimaryFrameFor(content, &frame);
00153         if (!frame)
00154           return rect;
00155 
00156         nsIView* view;
00157         nsPoint offset;
00158         frame->GetOffsetFromView(offset, &view);
00159         if (view) {
00160           nsPoint widgetOffset(0, 0);
00161           nsIWidget* widget = view->GetNearestWidget(&widgetOffset);
00162           if (widget) {
00163             nsRect oldBox(0,0,0,0);
00164             widget->WidgetToScreen(oldBox, rect);
00165           }
00166           
00167           rect.x += NSTwipsToIntPixels(offset.x+widgetOffset.x, scale);
00168           rect.y += NSTwipsToIntPixels(offset.y+widgetOffset.y, scale);
00169         }
00170         
00171         size = frame->GetSize();
00172         rect.width = NSTwipsToIntPixels(size.width, scale);
00173         rect.height = NSTwipsToIntPixels(size.height, scale);
00174       }
00175     }
00176   }
00177   
00178   return rect;
00179 }
00180 
00183 
00184 NS_IMETHODIMP
00185 nsFormFillController::AttachToBrowser(nsIDocShell *aDocShell, nsIAutoCompletePopup *aPopup)
00186 {
00187   NS_ENSURE_TRUE(aDocShell && aPopup, NS_ERROR_ILLEGAL_VALUE);
00188   
00189   mDocShells->AppendElement(aDocShell);
00190   mPopups->AppendElement(aPopup);
00191   
00192   // Listen for focus events on the domWindow of the docShell
00193   nsCOMPtr<nsIDOMWindow> domWindow = GetWindowForDocShell(aDocShell);
00194   AddWindowListeners(domWindow);
00195 
00196   return NS_OK;
00197 }
00198 
00199 NS_IMETHODIMP
00200 nsFormFillController::DetachFromBrowser(nsIDocShell *aDocShell)
00201 {
00202   PRInt32 index = GetIndexOfDocShell(aDocShell);
00203   NS_ENSURE_TRUE(index >= 0, NS_ERROR_FAILURE);
00204   
00205   // Stop listening for focus events on the domWindow of the docShell
00206   nsCOMPtr<nsIDocShell> docShell;
00207   mDocShells->GetElementAt(index, getter_AddRefs(docShell));
00208   nsCOMPtr<nsIDOMWindow> domWindow = GetWindowForDocShell(docShell);
00209   RemoveWindowListeners(domWindow);
00210     
00211   mDocShells->RemoveElementAt(index);
00212   mPopups->RemoveElementAt(index);
00213   
00214   return NS_OK;
00215 }
00216 
00219 
00220 NS_IMETHODIMP
00221 nsFormFillController::GetPopup(nsIAutoCompletePopup **aPopup)
00222 {
00223   *aPopup = mFocusedPopup;
00224   NS_IF_ADDREF(*aPopup);
00225   return NS_OK;
00226 }
00227 
00228 NS_IMETHODIMP
00229 nsFormFillController::GetController(nsIAutoCompleteController **aController)
00230 {
00231   *aController = mController;
00232   NS_IF_ADDREF(*aController);
00233   return NS_OK;
00234 }
00235 
00236 NS_IMETHODIMP
00237 nsFormFillController::GetPopupOpen(PRBool *aPopupOpen)
00238 {
00239   if (mFocusedPopup)
00240     mFocusedPopup->GetPopupOpen(aPopupOpen);
00241   return NS_OK;
00242 }
00243 
00244 NS_IMETHODIMP
00245 nsFormFillController::SetPopupOpen(PRBool aPopupOpen)
00246 {
00247   if (mFocusedPopup) {
00248     if (aPopupOpen) {
00249       // make sure input field is visible before showing popup (bug 320938)
00250       nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
00251       NS_ENSURE_STATE(content);
00252       nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(mFocusedInput);
00253       NS_ENSURE_STATE(docShell);
00254       nsCOMPtr<nsIPresShell> presShell;
00255       docShell->GetPresShell(getter_AddRefs(presShell));
00256       NS_ENSURE_STATE(presShell);
00257       nsIFrame *frame = nsnull;
00258       presShell->GetPrimaryFrameFor(content.get(), &frame);
00259       if (frame)
00260         presShell->ScrollFrameIntoView(frame,
00261                                        NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
00262                                        NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
00263 
00264       nsRect popupRect = GetScreenOrigin(mFocusedInput);
00265       mFocusedPopup->OpenPopup(this, popupRect.x, popupRect.y+popupRect.height, popupRect.width);
00266     } else
00267       mFocusedPopup->ClosePopup();
00268   }
00269 
00270   return NS_OK;
00271 }
00272 
00273 NS_IMETHODIMP
00274 nsFormFillController::GetDisableAutoComplete(PRBool *aDisableAutoComplete)
00275 {
00276   *aDisableAutoComplete = mDisableAutoComplete;
00277   return NS_OK;
00278 }
00279 
00280 NS_IMETHODIMP
00281 nsFormFillController::SetDisableAutoComplete(PRBool aDisableAutoComplete)
00282 {
00283   mDisableAutoComplete = aDisableAutoComplete;
00284   return NS_OK;
00285 }
00286 
00287 NS_IMETHODIMP
00288 nsFormFillController::GetCompleteDefaultIndex(PRBool *aCompleteDefaultIndex)
00289 {
00290   *aCompleteDefaultIndex = mCompleteDefaultIndex;
00291   return NS_OK;
00292 }
00293 
00294 NS_IMETHODIMP
00295 nsFormFillController::SetCompleteDefaultIndex(PRBool aCompleteDefaultIndex)
00296 {
00297   mCompleteDefaultIndex = aCompleteDefaultIndex;
00298   return NS_OK;
00299 }
00300 
00301 NS_IMETHODIMP
00302 nsFormFillController::GetCompleteSelectedIndex(PRBool *aCompleteSelectedIndex)
00303 {
00304   *aCompleteSelectedIndex = mCompleteSelectedIndex;
00305   return NS_OK;
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsFormFillController::SetCompleteSelectedIndex(PRBool aCompleteSelectedIndex)
00310 {
00311   mCompleteSelectedIndex = aCompleteSelectedIndex;
00312   return NS_OK;
00313 }
00314 
00315 NS_IMETHODIMP
00316 nsFormFillController::GetForceComplete(PRBool *aForceComplete)
00317 {
00318   *aForceComplete = mForceComplete;
00319   return NS_OK;
00320 }
00321 
00322 NS_IMETHODIMP nsFormFillController::SetForceComplete(PRBool aForceComplete)
00323 {
00324   mForceComplete = aForceComplete;
00325   return NS_OK;
00326 }
00327 
00328 NS_IMETHODIMP
00329 nsFormFillController::GetMinResultsForPopup(PRUint32 *aMinResultsForPopup)
00330 {
00331   *aMinResultsForPopup = mMinResultsForPopup;
00332   return NS_OK;
00333 }
00334 
00335 NS_IMETHODIMP nsFormFillController::SetMinResultsForPopup(PRUint32 aMinResultsForPopup)
00336 {
00337   mMinResultsForPopup = aMinResultsForPopup;
00338   return NS_OK;
00339 }
00340 
00341 NS_IMETHODIMP
00342 nsFormFillController::GetMaxRows(PRUint32 *aMaxRows)
00343 {
00344   *aMaxRows = mMaxRows;
00345   return NS_OK;
00346 }
00347 
00348 NS_IMETHODIMP
00349 nsFormFillController::SetMaxRows(PRUint32 aMaxRows)
00350 {
00351   mMaxRows = aMaxRows;
00352   return NS_OK;
00353 }
00354 
00355 NS_IMETHODIMP
00356 nsFormFillController::GetShowCommentColumn(PRUint32 *aShowCommentColumn)
00357 {
00358   *aShowCommentColumn = PR_FALSE;
00359   return NS_OK;
00360 }
00361 
00362 NS_IMETHODIMP nsFormFillController::SetShowCommentColumn(PRUint32 aShowCommentColumn)
00363 {
00364   return NS_ERROR_NOT_IMPLEMENTED;
00365 }
00366 
00367 NS_IMETHODIMP
00368 nsFormFillController::GetTimeout(PRUint32 *aTimeout)
00369 {
00370   *aTimeout = mTimeout;
00371   return NS_OK;
00372 }
00373 
00374 NS_IMETHODIMP nsFormFillController::SetTimeout(PRUint32 aTimeout)
00375 {
00376   mTimeout = aTimeout;
00377   return NS_OK;
00378 }
00379 
00380 NS_IMETHODIMP
00381 nsFormFillController::SetSearchParam(const nsAString &aSearchParam)
00382 {
00383   return NS_ERROR_NOT_IMPLEMENTED;
00384 }
00385 
00386 NS_IMETHODIMP
00387 nsFormFillController::GetSearchParam(nsAString &aSearchParam)
00388 {
00389   if (!mFocusedInput) {
00390     NS_WARNING("mFocusedInput is null for some reason! avoiding a crash. should find out why... - ben");
00391     return NS_ERROR_FAILURE; // XXX why? fix me. 
00392   }
00393     
00394   mFocusedInput->GetName(aSearchParam);
00395   if (aSearchParam.IsEmpty())
00396     mFocusedInput->GetId(aSearchParam);
00397   
00398   return NS_OK;
00399 }
00400 
00401 NS_IMETHODIMP
00402 nsFormFillController::GetSearchCount(PRUint32 *aSearchCount)
00403 {
00404   *aSearchCount = 1;
00405   return NS_OK;
00406 }
00407 
00408 NS_IMETHODIMP
00409 nsFormFillController::GetSearchAt(PRUint32 index, nsACString & _retval)
00410 {
00411   _retval.Assign("form-history");
00412   return NS_OK;
00413 }
00414 
00415 NS_IMETHODIMP
00416 nsFormFillController::GetTextValue(nsAString & aTextValue)
00417 {
00418   if (mFocusedInput) {
00419     mFocusedInput->GetValue(aTextValue);
00420   } else {
00421     aTextValue.Truncate();
00422   }
00423   return NS_OK;
00424 }
00425 
00426 NS_IMETHODIMP
00427 nsFormFillController::SetTextValue(const nsAString & aTextValue)
00428 {
00429   if (mFocusedInput) {
00430     mSuppressOnInput = PR_TRUE;
00431     mFocusedInput->SetValue(aTextValue);
00432     mSuppressOnInput = PR_FALSE;
00433   }
00434   return NS_OK;
00435 }
00436 
00437 NS_IMETHODIMP
00438 nsFormFillController::GetSelectionStart(PRInt32 *aSelectionStart)
00439 {
00440   nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(mFocusedInput);
00441   if (input)
00442     input->GetSelectionStart(aSelectionStart);
00443   return NS_OK;
00444 }
00445 
00446 NS_IMETHODIMP
00447 nsFormFillController::GetSelectionEnd(PRInt32 *aSelectionEnd)
00448 {
00449   nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(mFocusedInput);
00450   if (input)
00451     input->GetSelectionEnd(aSelectionEnd);
00452   return NS_OK;
00453 }
00454 
00455 NS_IMETHODIMP
00456 nsFormFillController::SelectTextRange(PRInt32 aStartIndex, PRInt32 aEndIndex)
00457 {
00458   nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(mFocusedInput);
00459   if (input)
00460     input->SetSelectionRange(aStartIndex, aEndIndex);
00461   return NS_OK;
00462 }
00463 
00464 NS_IMETHODIMP
00465 nsFormFillController::OnSearchComplete()
00466 {
00467   return NS_OK;
00468 }
00469 
00470 NS_IMETHODIMP
00471 nsFormFillController::OnTextEntered(PRBool* aPrevent)
00472 {
00473   NS_ENSURE_ARG(aPrevent);
00474   NS_ENSURE_TRUE(mFocusedInput, NS_OK);
00475   // Fire off a DOMAutoComplete event
00476   nsCOMPtr<nsIDOMDocument> domDoc;
00477   mFocusedInput->GetOwnerDocument(getter_AddRefs(domDoc));
00478 
00479   nsCOMPtr<nsIDOMDocumentEvent> doc = do_QueryInterface(domDoc);
00480   NS_ENSURE_STATE(doc);
00481 
00482   nsCOMPtr<nsIDOMEvent> event;
00483   doc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
00484   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
00485   NS_ENSURE_STATE(privateEvent);
00486 
00487   event->InitEvent(NS_LITERAL_STRING("DOMAutoComplete"), PR_TRUE, PR_TRUE);
00488 
00489   // XXXjst: We mark this event as a trusted event, it's up to the
00490   // callers of this to ensure that it's only called from trusted
00491   // code.
00492   privateEvent->SetTrusted(PR_TRUE);
00493 
00494   nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(mFocusedInput);
00495 
00496   PRBool defaultActionEnabled;
00497   targ->DispatchEvent(event, &defaultActionEnabled);
00498   *aPrevent = !defaultActionEnabled;
00499   return NS_OK;
00500 }
00501 
00502 NS_IMETHODIMP
00503 nsFormFillController::OnTextReverted(PRBool *_retval)
00504 {
00505   return NS_OK;
00506 }
00507 
00510 
00511 NS_IMETHODIMP
00512 nsFormFillController::GetConsumeRollupEvent(PRBool *aConsumeRollupEvent)
00513 {
00514   *aConsumeRollupEvent = PR_FALSE;
00515   return NS_OK;
00516 }
00517 
00520 
00521 
00522 NS_IMETHODIMP
00523 nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAString &aSearchParam,
00524                                   nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
00525 {
00526   nsCOMPtr<nsIAutoCompleteResult> result;
00527 
00528 #ifdef MOZ_PLACES
00529   // This assumes that FormHistory uses nsIAutoCompleteSimpleResult,
00530   // while PasswordManager does not.
00531   nsCOMPtr<nsIAutoCompleteSimpleResult> historyResult;
00532 #else
00533   nsCOMPtr<nsIAutoCompleteMdbResult2> historyResult;
00534 #endif
00535   historyResult = do_QueryInterface(aPreviousResult);
00536 
00537   nsPasswordManager* passMgr = nsPasswordManager::GetInstance();
00538   if (!passMgr)
00539     return NS_ERROR_OUT_OF_MEMORY;
00540 
00541   // Only hand off a previous result to the password manager if it's
00542   // a password manager result (i.e. not an nsIAutoCompleteMdb/SimpleResult).
00543 
00544   if (!passMgr->AutoCompleteSearch(aSearchString,
00545                                    historyResult ? nsnull : aPreviousResult,
00546                                    mFocusedInput,
00547                                    getter_AddRefs(result)))
00548   {
00549     nsFormHistory *history = nsFormHistory::GetInstance();
00550     if (history) {
00551       history->AutoCompleteSearch(aSearchParam,
00552                                   aSearchString,
00553                                   historyResult,
00554                                   getter_AddRefs(result));
00555     }
00556   }
00557   NS_RELEASE(passMgr);
00558 
00559   aListener->OnSearchResult(this, result);  
00560   
00561   return NS_OK;
00562 }
00563 
00564 NS_IMETHODIMP
00565 nsFormFillController::StopSearch()
00566 {
00567   return NS_OK;
00568 }
00569 
00572 
00573 NS_IMETHODIMP
00574 nsFormFillController::HandleEvent(nsIDOMEvent* aEvent)
00575 {
00576   return NS_OK; 
00577 }
00578 
00581 
00582 NS_IMETHODIMP
00583 nsFormFillController::Focus(nsIDOMEvent* aEvent)
00584 {
00585   nsCOMPtr<nsIDOMEventTarget> target;
00586   aEvent->GetTarget(getter_AddRefs(target));
00587   
00588   nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(target);
00589   if (!input)
00590     return NS_OK;
00591     
00592     nsAutoString type;
00593     input->GetType(type);
00594 
00595     PRBool isReadOnly = PR_FALSE;
00596     input->GetReadOnly(&isReadOnly);
00597                                   
00598     nsAutoString autocomplete; 
00599     input->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
00600     if (type.Equals(NS_LITERAL_STRING("text")) && !isReadOnly &&
00601         !autocomplete.EqualsIgnoreCase("off")) {
00602 
00603       nsCOMPtr<nsIDOMHTMLFormElement> form;
00604       input->GetForm(getter_AddRefs(form));
00605       if (form)
00606         form->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
00607 
00608       if (!form || !autocomplete.EqualsIgnoreCase("off"))
00609         StartControllingInput(input);
00610     }
00611     
00612   return NS_OK;
00613 }
00614 
00615 NS_IMETHODIMP
00616 nsFormFillController::Blur(nsIDOMEvent* aEvent)
00617 {
00618   if (mFocusedInput)
00619     StopControllingInput();
00620   
00621   return NS_OK;
00622 }
00623 
00626 
00627 NS_IMETHODIMP
00628 nsFormFillController::KeyDown(nsIDOMEvent* aEvent)
00629 {
00630   return NS_OK;
00631 } 
00632 
00633 NS_IMETHODIMP
00634 nsFormFillController::KeyUp(nsIDOMEvent* aEvent)
00635 {
00636   return NS_OK;
00637 }
00638 
00639 NS_IMETHODIMP
00640 nsFormFillController::KeyPress(nsIDOMEvent* aEvent)
00641 {
00642   NS_ASSERTION(mController, "should have a controller!");
00643   if (!mFocusedInput || !mController)
00644     return NS_OK;
00645 
00646   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
00647   if (!keyEvent)
00648     return NS_ERROR_FAILURE;
00649 
00650   PRBool cancel = PR_FALSE;
00651 
00652   PRUint32 k;
00653   keyEvent->GetKeyCode(&k);
00654   switch (k) {
00655   case nsIDOMKeyEvent::DOM_VK_DELETE:
00656 #ifndef XP_MACOSX
00657     mController->HandleDelete(&cancel);
00658     break;
00659   case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
00660     mController->HandleText(PR_FALSE);
00661     break;
00662 #else
00663   case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
00664     {
00665       PRBool isShift = PR_FALSE;
00666       keyEvent->GetShiftKey(&isShift);
00667 
00668       if (isShift)
00669         mController->HandleDelete(&cancel);
00670       else
00671         mController->HandleText(PR_FALSE);
00672 
00673       break;
00674     }
00675 #endif
00676   case nsIDOMKeyEvent::DOM_VK_UP:
00677     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_UP, &cancel);
00678     break;
00679   case nsIDOMKeyEvent::DOM_VK_DOWN:
00680     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_DOWN, &cancel);
00681     break;
00682   case nsIDOMKeyEvent::DOM_VK_LEFT:
00683     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_LEFT, &cancel);
00684     break;
00685   case nsIDOMKeyEvent::DOM_VK_RIGHT:
00686     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_RIGHT, &cancel);
00687     break;
00688   case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
00689     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_PAGE_UP, &cancel);
00690     break;
00691   case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
00692     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_PAGE_DOWN, &cancel);
00693     break;
00694   case nsIDOMKeyEvent::DOM_VK_ESCAPE:
00695     mController->HandleEscape(&cancel);
00696     break;
00697   case nsIDOMKeyEvent::DOM_VK_TAB:
00698     mController->HandleTab();
00699     cancel = PR_FALSE;
00700     break;
00701   case nsIDOMKeyEvent::DOM_VK_RETURN:
00702     mController->HandleEnter(&cancel);
00703     break;
00704   }
00705   
00706   if (cancel) {
00707     aEvent->StopPropagation();
00708     aEvent->PreventDefault();
00709   }
00710   
00711   return NS_OK;
00712 }
00713 
00716 
00717 NS_IMETHODIMP
00718 nsFormFillController::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
00719 {
00720   NS_ASSERTION(mController, "should have a controller!");
00721 
00722   if (mController && mFocusedInput)
00723     mController->HandleStartComposition();
00724 
00725   return NS_OK;
00726 }
00727 
00728 NS_IMETHODIMP
00729 nsFormFillController::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
00730 {
00731   NS_ASSERTION(mController, "should have a controller!");
00732 
00733   if (mController && mFocusedInput)
00734     mController->HandleEndComposition();
00735 
00736   return NS_OK;
00737 }
00738 
00739 NS_IMETHODIMP
00740 nsFormFillController::HandleQueryComposition(nsIDOMEvent* aCompositionEvent)
00741 {
00742   return NS_OK;
00743 }
00744 
00745 NS_IMETHODIMP
00746 nsFormFillController::HandleQueryReconversion(nsIDOMEvent* aCompositionEvent)
00747 {
00748   return NS_OK;
00749 }
00750 
00751 NS_IMETHODIMP
00752 nsFormFillController::HandleQueryCaretRect(nsIDOMEvent* aCompostionEvent)
00753 {
00754   return NS_OK;
00755 }
00756 
00759 
00760 NS_IMETHODIMP
00761 nsFormFillController::Submit(nsIDOMEvent* aEvent)
00762 {
00763   if (mFocusedInput)
00764     StopControllingInput();
00765 
00766   return NS_OK;
00767 }
00768 
00769 NS_IMETHODIMP
00770 nsFormFillController::Reset(nsIDOMEvent* aEvent)
00771 {
00772   return NS_OK;
00773 }
00774 
00775 NS_IMETHODIMP
00776 nsFormFillController::Change(nsIDOMEvent* aEvent)
00777 {
00778   return NS_OK;
00779 }
00780 
00781 NS_IMETHODIMP
00782 nsFormFillController::Select(nsIDOMEvent* aEvent)
00783 {
00784   return NS_OK;
00785 }
00786 
00787 NS_IMETHODIMP
00788 nsFormFillController::Input(nsIDOMEvent* aEvent)
00789 {
00790   if (mSuppressOnInput || !mController || !mFocusedInput)
00791     return NS_OK;
00792 
00793   return mController->HandleText(PR_FALSE);
00794 }
00795 
00798 
00799 NS_IMETHODIMP
00800 nsFormFillController::MouseDown(nsIDOMEvent* aMouseEvent)
00801 {
00802   mIgnoreClick = PR_FALSE;
00803 
00804   nsCOMPtr<nsIDOMEventTarget> target;
00805   aMouseEvent->GetTarget(getter_AddRefs(target));
00806 
00807   nsCOMPtr<nsIDOMHTMLInputElement> targetInput = do_QueryInterface(target);
00808   if (!targetInput || (targetInput && targetInput != mFocusedInput)) {
00809     // A new input will be taking focus.  Ignore the first click
00810     // so that the popup is not shown.
00811     mIgnoreClick = PR_TRUE;
00812     return NS_OK;
00813   }
00814 
00815   return NS_OK;
00816 }
00817 
00818 NS_IMETHODIMP
00819 nsFormFillController::MouseUp(nsIDOMEvent* aMouseEvent)
00820 {
00821   return NS_OK;
00822 }
00823 
00824 NS_IMETHODIMP
00825 nsFormFillController::MouseClick(nsIDOMEvent* aMouseEvent)
00826 {
00827   if (mIgnoreClick) {
00828     mIgnoreClick = PR_FALSE;
00829     return NS_OK;
00830   }
00831 
00832   if (!mFocusedInput)
00833     return NS_OK;
00834 
00835   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
00836   if (!mouseEvent)
00837     return NS_ERROR_FAILURE;
00838 
00839   PRUint16 button;
00840   mouseEvent->GetButton(&button);
00841   if (button != 0)
00842     return NS_OK;
00843 
00844   PRBool isOpen = PR_FALSE;
00845   GetPopupOpen(&isOpen);
00846   if (isOpen)
00847     return NS_OK;
00848 
00849   nsCOMPtr<nsIAutoCompleteInput> input;
00850   mController->GetInput(getter_AddRefs(input));
00851   if (!input)
00852     return NS_OK;
00853 
00854   nsAutoString value;
00855   input->GetTextValue(value);
00856   if (value.Length() > 0) {
00857     // Show the popup with a filtered result set
00858     mController->SetSearchString(EmptyString());
00859     mController->HandleText(PR_TRUE);
00860   } else {
00861     // Show the popup with the complete result set.  Can't use HandleText()
00862     // because it doesn't display the popup if the input is blank.
00863     PRBool cancel = PR_FALSE;
00864     mController->HandleKeyNavigation(nsIAutoCompleteController::KEY_DOWN, &cancel);
00865   }
00866 
00867   return NS_OK;
00868 }
00869 
00870 NS_IMETHODIMP
00871 nsFormFillController::MouseDblClick(nsIDOMEvent* aMouseEvent)
00872 { 
00873     return NS_OK;
00874 }
00875 
00876 NS_IMETHODIMP
00877 nsFormFillController::MouseOver(nsIDOMEvent* aMouseEvent)
00878 {
00879   return NS_OK;
00880 }
00881 
00882 NS_IMETHODIMP
00883 nsFormFillController::MouseOut(nsIDOMEvent* aMouseEvent)
00884 {
00885   return NS_OK;
00886 }
00887 
00890 
00891 NS_IMETHODIMP
00892 nsFormFillController::Load(nsIDOMEvent *aLoadEvent)
00893 {
00894   return NS_OK;
00895 }
00896 
00897 NS_IMETHODIMP
00898 nsFormFillController::BeforeUnload(nsIDOMEvent *aLoadEvent)
00899 {
00900   return NS_OK;
00901 }
00902 
00903 NS_IMETHODIMP
00904 nsFormFillController::Unload(nsIDOMEvent *aLoadEvent)
00905 {
00906   if (mFocusedInput) {
00907     nsCOMPtr<nsIDOMEventTarget> target;
00908     aLoadEvent->GetTarget(getter_AddRefs(target));
00909 
00910     nsCOMPtr<nsIDOMDocument> eventDoc = do_QueryInterface(target);
00911     nsCOMPtr<nsIDOMDocument> inputDoc;
00912     mFocusedInput->GetOwnerDocument(getter_AddRefs(inputDoc));
00913 
00914     if (eventDoc == inputDoc)
00915       StopControllingInput();
00916   }
00917 
00918   return NS_OK;
00919 }
00920 
00921 NS_IMETHODIMP
00922 nsFormFillController::Abort(nsIDOMEvent *aLoadEvent)
00923 {
00924   return NS_OK;
00925 }
00926 
00927 NS_IMETHODIMP
00928 nsFormFillController::Error(nsIDOMEvent *aLoadEvent)
00929 {
00930   return NS_OK;
00931 }
00932 
00933 NS_IMETHODIMP
00934 nsFormFillController::ContextMenu(nsIDOMEvent* aContextMenuEvent)
00935 {
00936   if (mFocusedPopup)
00937     mFocusedPopup->ClosePopup();
00938   return NS_OK;
00939 }
00940 
00943 
00944 void
00945 nsFormFillController::AddWindowListeners(nsIDOMWindow *aWindow)
00946 {
00947   if (!aWindow)
00948     return;
00949 
00950   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aWindow));
00951   nsIChromeEventHandler* chromeEventHandler = nsnull;
00952   if (privateDOMWindow)
00953     chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
00954 
00955   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(chromeEventHandler));
00956 
00957   if (!target)
00958     return;
00959 
00960   target->AddEventListener(NS_LITERAL_STRING("focus"),
00961                            NS_STATIC_CAST(nsIDOMFocusListener *, this),
00962                            PR_TRUE);
00963 
00964   target->AddEventListener(NS_LITERAL_STRING("blur"),
00965                            NS_STATIC_CAST(nsIDOMFocusListener *, this),
00966                            PR_TRUE);
00967 
00968   target->AddEventListener(NS_LITERAL_STRING("mousedown"),
00969                            NS_STATIC_CAST(nsIDOMMouseListener *, this),
00970                            PR_TRUE);
00971 
00972   target->AddEventListener(NS_LITERAL_STRING("click"),
00973                            NS_STATIC_CAST(nsIDOMMouseListener *, this),
00974                            PR_TRUE);
00975 
00976   target->AddEventListener(NS_LITERAL_STRING("input"),
00977                            NS_STATIC_CAST(nsIDOMFormListener *, this),
00978                            PR_TRUE);
00979 
00980   target->AddEventListener(NS_LITERAL_STRING("unload"),
00981                            NS_STATIC_CAST(nsIDOMLoadListener *, this),
00982                            PR_TRUE);
00983 
00984   target->AddEventListener(NS_LITERAL_STRING("compositionstart"),
00985                            NS_STATIC_CAST(nsIDOMCompositionListener *, this),
00986                            PR_TRUE);
00987 
00988   target->AddEventListener(NS_LITERAL_STRING("compositionend"),
00989                            NS_STATIC_CAST(nsIDOMCompositionListener *, this),
00990                            PR_TRUE);
00991 
00992   target->AddEventListener(NS_LITERAL_STRING("contextmenu"),
00993                            NS_STATIC_CAST(nsIDOMContextMenuListener *, this),
00994                            PR_TRUE);
00995 }
00996 
00997 void
00998 nsFormFillController::RemoveWindowListeners(nsIDOMWindow *aWindow)
00999 {
01000   if (!aWindow)
01001     return;
01002 
01003   StopControllingInput();
01004   
01005   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aWindow));
01006   nsIChromeEventHandler* chromeEventHandler = nsnull;
01007   if (privateDOMWindow)
01008     chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
01009   
01010   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(chromeEventHandler));
01011 
01012   if (!target)
01013     return;
01014 
01015   target->RemoveEventListener(NS_LITERAL_STRING("focus"),
01016                               NS_STATIC_CAST(nsIDOMFocusListener *, this),
01017                               PR_TRUE);
01018 
01019   target->RemoveEventListener(NS_LITERAL_STRING("blur"),
01020                               NS_STATIC_CAST(nsIDOMFocusListener *, this),
01021                               PR_TRUE);
01022 
01023   target->RemoveEventListener(NS_LITERAL_STRING("mousedown"),
01024                               NS_STATIC_CAST(nsIDOMMouseListener *, this),
01025                               PR_TRUE);
01026 
01027   target->RemoveEventListener(NS_LITERAL_STRING("click"),
01028                               NS_STATIC_CAST(nsIDOMMouseListener *, this),
01029                               PR_TRUE);
01030 
01031   target->RemoveEventListener(NS_LITERAL_STRING("input"),
01032                               NS_STATIC_CAST(nsIDOMFormListener *, this),
01033                               PR_TRUE);
01034 
01035   target->RemoveEventListener(NS_LITERAL_STRING("unload"),
01036                               NS_STATIC_CAST(nsIDOMLoadListener *, this),
01037                               PR_TRUE);
01038 
01039   target->RemoveEventListener(NS_LITERAL_STRING("compositionstart"),
01040                               NS_STATIC_CAST(nsIDOMCompositionListener *, this),
01041                               PR_TRUE);
01042 
01043   target->RemoveEventListener(NS_LITERAL_STRING("compositionend"),
01044                               NS_STATIC_CAST(nsIDOMCompositionListener *, this),
01045                               PR_TRUE);
01046 
01047   target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"),
01048                               NS_STATIC_CAST(nsIDOMContextMenuListener *, this),
01049                               PR_TRUE);
01050 }
01051 
01052 void
01053 nsFormFillController::AddKeyListener(nsIDOMHTMLInputElement *aInput)
01054 {
01055   if (!aInput)
01056     return;
01057 
01058     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aInput);
01059 
01060     target->AddEventListener(NS_LITERAL_STRING("keypress"),
01061                              NS_STATIC_CAST(nsIDOMKeyListener *, this),
01062                              PR_TRUE);
01063   }
01064 
01065 void
01066 nsFormFillController::RemoveKeyListener()
01067 {
01068   if (!mFocusedInput)
01069     return;
01070 
01071     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mFocusedInput);
01072 
01073     target->RemoveEventListener(NS_LITERAL_STRING("keypress"),
01074                                 NS_STATIC_CAST(nsIDOMKeyListener *, this),
01075                                 PR_TRUE);
01076 }
01077 
01078 void
01079 nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput)
01080 {
01081   // Make sure we're not still attached to an input
01082   StopControllingInput(); 
01083 
01084   // Find the currently focused docShell
01085   nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(aInput);
01086   PRInt32 index = GetIndexOfDocShell(docShell);
01087   if (index < 0)
01088     return;
01089   
01090   // Cache the popup for the focused docShell
01091   mPopups->GetElementAt(index, getter_AddRefs(mFocusedPopup));
01092   
01093   AddKeyListener(aInput);
01094   mFocusedInput = aInput;
01095 
01096   // Now we are the autocomplete controller's bitch
01097   mController->SetInput(this);
01098 }
01099 
01100 void
01101 nsFormFillController::StopControllingInput()
01102 {
01103   RemoveKeyListener();
01104 
01105   // Reset the controller's input, but not if it has been switched
01106   // to another input already, which might happen if the user switches
01107   // focus by clicking another autocomplete textbox
01108   nsCOMPtr<nsIAutoCompleteInput> input;
01109   mController->GetInput(getter_AddRefs(input));
01110   if (input == this)
01111     mController->SetInput(nsnull);
01112 
01113   mFocusedInput = nsnull;
01114   mFocusedPopup = nsnull;
01115 }
01116 
01117 nsIDocShell *
01118 nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
01119 {
01120   nsCOMPtr<nsIDOMDocument> domDoc;
01121   aInput->GetOwnerDocument(getter_AddRefs(domDoc));
01122   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
01123   NS_ENSURE_TRUE(doc, nsnull);
01124   nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(doc->GetScriptGlobalObject());
01125   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
01126   return docShell;
01127 }
01128 
01129 nsIDOMWindow *
01130 nsFormFillController::GetWindowForDocShell(nsIDocShell *aDocShell)
01131 {
01132   nsCOMPtr<nsIContentViewer> contentViewer;
01133   aDocShell->GetContentViewer(getter_AddRefs(contentViewer));
01134   NS_ENSURE_TRUE(contentViewer, nsnull);
01135 
01136   nsCOMPtr<nsIDOMDocument> domDoc;
01137   contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
01138   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
01139   NS_ENSURE_TRUE(doc, nsnull);
01140 
01141   nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(doc->GetScriptGlobalObject());
01142   return domWindow;
01143 }
01144 
01145 PRInt32
01146 nsFormFillController::GetIndexOfDocShell(nsIDocShell *aDocShell)
01147 {
01148   if (!aDocShell)
01149     return -1;
01150 
01151   // Loop through our cached docShells looking for the given docShell
01152   PRUint32 count;
01153   mDocShells->Count(&count);
01154   for (PRUint32 i = 0; i < count; ++i) {
01155     nsCOMPtr<nsIDocShell> docShell;
01156     mDocShells->GetElementAt(i, getter_AddRefs(docShell));
01157     if (docShell == aDocShell)
01158       return i;
01159   }
01160 
01161   // Recursively check the parent docShell of this one
01162   nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(aDocShell);
01163   nsCOMPtr<nsIDocShellTreeItem> parentItem;
01164   treeItem->GetParent(getter_AddRefs(parentItem));
01165   if (parentItem) {
01166     nsCOMPtr<nsIDocShell> parentShell = do_QueryInterface(parentItem);
01167     return GetIndexOfDocShell(parentShell);
01168   }
01169     
01170   return -1;
01171 }