toolkit/components/satchel/nsFormFillController.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsFormFillController.h"
     8 #include "mozilla/dom/Element.h"
     9 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
    10 #include "nsIFormAutoComplete.h"
    11 #include "nsIInputListAutoComplete.h"
    12 #include "nsIAutoCompleteSimpleResult.h"
    13 #include "nsString.h"
    14 #include "nsReadableUtils.h"
    15 #include "nsIServiceManager.h"
    16 #include "nsIInterfaceRequestor.h"
    17 #include "nsIInterfaceRequestorUtils.h"
    18 #include "nsIDocShellTreeItem.h"
    19 #include "nsPIDOMWindow.h"
    20 #include "nsIWebNavigation.h"
    21 #include "nsIContentViewer.h"
    22 #include "nsIDOMKeyEvent.h"
    23 #include "nsIDOMDocument.h"
    24 #include "nsIDOMElement.h"
    25 #include "nsIFormControl.h"
    26 #include "nsIDocument.h"
    27 #include "nsIContent.h"
    28 #include "nsIPresShell.h"
    29 #include "nsRect.h"
    30 #include "nsIDOMHTMLFormElement.h"
    31 #include "nsILoginManager.h"
    32 #include "nsIDOMMouseEvent.h"
    33 #include "mozilla/ModuleUtils.h"
    34 #include "nsToolkitCompsCID.h"
    35 #include "nsEmbedCID.h"
    36 #include "nsIDOMNSEditableElement.h"
    37 #include "nsContentUtils.h"
    38 #include "nsILoadContext.h"
    40 using namespace mozilla::dom;
    42 NS_IMPL_ISUPPORTS(nsFormFillController,
    43                   nsIFormFillController,
    44                   nsIAutoCompleteInput,
    45                   nsIAutoCompleteSearch,
    46                   nsIDOMEventListener,
    47                   nsIFormAutoCompleteObserver,
    48                   nsIMutationObserver)
    50 nsFormFillController::nsFormFillController() :
    51   mFocusedInput(nullptr),
    52   mFocusedInputNode(nullptr),
    53   mListNode(nullptr),
    54   mTimeout(50),
    55   mMinResultsForPopup(1),
    56   mMaxRows(0),
    57   mDisableAutoComplete(false),
    58   mCompleteDefaultIndex(false),
    59   mCompleteSelectedIndex(false),
    60   mForceComplete(false),
    61   mSuppressOnInput(false)
    62 {
    63   mController = do_GetService("@mozilla.org/autocomplete/controller;1");
    64 }
    66 struct PwmgrInputsEnumData
    67 {
    68   PwmgrInputsEnumData(nsFormFillController* aFFC, nsIDocument* aDoc)
    69   : mFFC(aFFC), mDoc(aDoc) {}
    71   nsFormFillController* mFFC;
    72   nsCOMPtr<nsIDocument> mDoc;
    73 };
    75 nsFormFillController::~nsFormFillController()
    76 {
    77   if (mListNode) {
    78     mListNode->RemoveMutationObserver(this);
    79     mListNode = nullptr;
    80   }
    81   if (mFocusedInputNode) {
    82     MaybeRemoveMutationObserver(mFocusedInputNode);
    83     mFocusedInputNode = nullptr;
    84     mFocusedInput = nullptr;
    85   }
    86   PwmgrInputsEnumData ed(this, nullptr);
    87   mPwmgrInputs.Enumerate(RemoveForDocumentEnumerator, &ed);
    89   // Remove ourselves as a focus listener from all cached docShells
    90   uint32_t count = mDocShells.Length();
    91   for (uint32_t i = 0; i < count; ++i) {
    92     nsCOMPtr<nsIDOMWindow> domWindow = GetWindowForDocShell(mDocShells[i]);
    93     RemoveWindowListeners(domWindow);
    94   }
    95 }
    97 ////////////////////////////////////////////////////////////////////////
    98 //// nsIMutationObserver
    99 //
   101 void
   102 nsFormFillController::AttributeChanged(nsIDocument* aDocument,
   103                                        mozilla::dom::Element* aElement,
   104                                        int32_t aNameSpaceID,
   105                                        nsIAtom* aAttribute, int32_t aModType)
   106 {
   107   if (mListNode && mListNode->Contains(aElement)) {
   108     RevalidateDataList();
   109   }
   110 }
   112 void
   113 nsFormFillController::ContentAppended(nsIDocument* aDocument,
   114                                       nsIContent* aContainer,
   115                                       nsIContent* aChild,
   116                                       int32_t aIndexInContainer)
   117 {
   118   if (mListNode && mListNode->Contains(aContainer)) {
   119     RevalidateDataList();
   120   }
   121 }
   123 void
   124 nsFormFillController::ContentInserted(nsIDocument* aDocument,
   125                                       nsIContent* aContainer,
   126                                       nsIContent* aChild,
   127                                       int32_t aIndexInContainer)
   128 {
   129   if (mListNode && mListNode->Contains(aContainer)) {
   130     RevalidateDataList();
   131   }
   132 }
   134 void
   135 nsFormFillController::ContentRemoved(nsIDocument* aDocument,
   136                                      nsIContent* aContainer,
   137                                      nsIContent* aChild,
   138                                      int32_t aIndexInContainer,
   139                                      nsIContent* aPreviousSibling)
   140 {
   141   if (mListNode && mListNode->Contains(aContainer)) {
   142     RevalidateDataList();
   143   }
   144 }
   146 void
   147 nsFormFillController::CharacterDataWillChange(nsIDocument* aDocument,
   148                                               nsIContent* aContent,
   149                                               CharacterDataChangeInfo* aInfo)
   150 {
   151 }
   153 void
   154 nsFormFillController::CharacterDataChanged(nsIDocument* aDocument,
   155                                            nsIContent* aContent,
   156                                            CharacterDataChangeInfo* aInfo)
   157 {
   158 }
   160 void
   161 nsFormFillController::AttributeWillChange(nsIDocument* aDocument,
   162                                           mozilla::dom::Element* aElement,
   163                                           int32_t aNameSpaceID,
   164                                           nsIAtom* aAttribute, int32_t aModType)
   165 {
   166 }
   168 void
   169 nsFormFillController::ParentChainChanged(nsIContent* aContent)
   170 {
   171 }
   173 void
   174 nsFormFillController::NodeWillBeDestroyed(const nsINode* aNode)
   175 {
   176   mPwmgrInputs.Remove(aNode);
   177   if (aNode == mListNode) {
   178     mListNode = nullptr;
   179     RevalidateDataList();
   180   } else if (aNode == mFocusedInputNode) {
   181     mFocusedInputNode = nullptr;
   182     mFocusedInput = nullptr;
   183   }
   184 }
   186 void
   187 nsFormFillController::MaybeRemoveMutationObserver(nsINode* aNode)
   188 {
   189   // Nodes being tracked in mPwmgrInputs will have their observers removed when
   190   // they stop being tracked. 
   191   bool dummy;
   192   if (!mPwmgrInputs.Get(aNode, &dummy)) {
   193     aNode->RemoveMutationObserver(this);
   194   }
   195 }
   197 ////////////////////////////////////////////////////////////////////////
   198 //// nsIFormFillController
   200 NS_IMETHODIMP
   201 nsFormFillController::AttachToBrowser(nsIDocShell *aDocShell, nsIAutoCompletePopup *aPopup)
   202 {
   203   NS_ENSURE_TRUE(aDocShell && aPopup, NS_ERROR_ILLEGAL_VALUE);
   205   mDocShells.AppendElement(aDocShell);
   206   mPopups.AppendElement(aPopup);
   208   // Listen for focus events on the domWindow of the docShell
   209   nsCOMPtr<nsIDOMWindow> domWindow = GetWindowForDocShell(aDocShell);
   210   AddWindowListeners(domWindow);
   212   return NS_OK;
   213 }
   215 NS_IMETHODIMP
   216 nsFormFillController::DetachFromBrowser(nsIDocShell *aDocShell)
   217 {
   218   int32_t index = GetIndexOfDocShell(aDocShell);
   219   NS_ENSURE_TRUE(index >= 0, NS_ERROR_FAILURE);
   221   // Stop listening for focus events on the domWindow of the docShell
   222   nsCOMPtr<nsIDOMWindow> domWindow =
   223     GetWindowForDocShell(mDocShells.SafeElementAt(index));
   224   RemoveWindowListeners(domWindow);
   226   mDocShells.RemoveElementAt(index);
   227   mPopups.RemoveElementAt(index);
   229   return NS_OK;
   230 }
   233 NS_IMETHODIMP
   234 nsFormFillController::MarkAsLoginManagerField(nsIDOMHTMLInputElement *aInput)
   235 {
   236   /*
   237    * The Login Manager can supply autocomplete results for username fields,
   238    * when a user has multiple logins stored for a site. It uses this
   239    * interface to indicate that the form manager shouldn't handle the
   240    * autocomplete. The form manager also checks for this tag when saving
   241    * form history (so it doesn't save usernames).
   242    */
   243   nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
   244   NS_ENSURE_STATE(node);
   245   mPwmgrInputs.Put(node, true);
   246   node->AddMutationObserverUnlessExists(this);
   248   if (!mLoginManager)
   249     mLoginManager = do_GetService("@mozilla.org/login-manager;1");
   251   return NS_OK;
   252 }
   255 ////////////////////////////////////////////////////////////////////////
   256 //// nsIAutoCompleteInput
   258 NS_IMETHODIMP
   259 nsFormFillController::GetPopup(nsIAutoCompletePopup **aPopup)
   260 {
   261   *aPopup = mFocusedPopup;
   262   NS_IF_ADDREF(*aPopup);
   263   return NS_OK;
   264 }
   266 NS_IMETHODIMP
   267 nsFormFillController::GetController(nsIAutoCompleteController **aController)
   268 {
   269   *aController = mController;
   270   NS_IF_ADDREF(*aController);
   271   return NS_OK;
   272 }
   274 NS_IMETHODIMP
   275 nsFormFillController::GetPopupOpen(bool *aPopupOpen)
   276 {
   277   if (mFocusedPopup)
   278     mFocusedPopup->GetPopupOpen(aPopupOpen);
   279   else
   280     *aPopupOpen = false;
   281   return NS_OK;
   282 }
   284 NS_IMETHODIMP
   285 nsFormFillController::SetPopupOpen(bool aPopupOpen)
   286 {
   287   if (mFocusedPopup) {
   288     if (aPopupOpen) {
   289       // make sure input field is visible before showing popup (bug 320938)
   290       nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
   291       NS_ENSURE_STATE(content);
   292       nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(mFocusedInput);
   293       NS_ENSURE_STATE(docShell);
   294       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   295       NS_ENSURE_STATE(presShell);
   296       presShell->ScrollContentIntoView(content,
   297                                        nsIPresShell::ScrollAxis(
   298                                          nsIPresShell::SCROLL_MINIMUM,
   299                                          nsIPresShell::SCROLL_IF_NOT_VISIBLE),
   300                                        nsIPresShell::ScrollAxis(
   301                                          nsIPresShell::SCROLL_MINIMUM,
   302                                          nsIPresShell::SCROLL_IF_NOT_VISIBLE),
   303                                        nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
   304       // mFocusedPopup can be destroyed after ScrollContentIntoView, see bug 420089
   305       if (mFocusedPopup) {
   306         nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mFocusedInput);
   307         mFocusedPopup->OpenAutocompletePopup(this, element);
   308       }
   309     } else
   310       mFocusedPopup->ClosePopup();
   311   }
   313   return NS_OK;
   314 }
   316 NS_IMETHODIMP
   317 nsFormFillController::GetDisableAutoComplete(bool *aDisableAutoComplete)
   318 {
   319   *aDisableAutoComplete = mDisableAutoComplete;
   320   return NS_OK;
   321 }
   323 NS_IMETHODIMP
   324 nsFormFillController::SetDisableAutoComplete(bool aDisableAutoComplete)
   325 {
   326   mDisableAutoComplete = aDisableAutoComplete;
   327   return NS_OK;
   328 }
   330 NS_IMETHODIMP
   331 nsFormFillController::GetCompleteDefaultIndex(bool *aCompleteDefaultIndex)
   332 {
   333   *aCompleteDefaultIndex = mCompleteDefaultIndex;
   334   return NS_OK;
   335 }
   337 NS_IMETHODIMP
   338 nsFormFillController::SetCompleteDefaultIndex(bool aCompleteDefaultIndex)
   339 {
   340   mCompleteDefaultIndex = aCompleteDefaultIndex;
   341   return NS_OK;
   342 }
   344 NS_IMETHODIMP
   345 nsFormFillController::GetCompleteSelectedIndex(bool *aCompleteSelectedIndex)
   346 {
   347   *aCompleteSelectedIndex = mCompleteSelectedIndex;
   348   return NS_OK;
   349 }
   351 NS_IMETHODIMP
   352 nsFormFillController::SetCompleteSelectedIndex(bool aCompleteSelectedIndex)
   353 {
   354   mCompleteSelectedIndex = aCompleteSelectedIndex;
   355   return NS_OK;
   356 }
   358 NS_IMETHODIMP
   359 nsFormFillController::GetForceComplete(bool *aForceComplete)
   360 {
   361   *aForceComplete = mForceComplete;
   362   return NS_OK;
   363 }
   365 NS_IMETHODIMP nsFormFillController::SetForceComplete(bool aForceComplete)
   366 {
   367   mForceComplete = aForceComplete;
   368   return NS_OK;
   369 }
   371 NS_IMETHODIMP
   372 nsFormFillController::GetMinResultsForPopup(uint32_t *aMinResultsForPopup)
   373 {
   374   *aMinResultsForPopup = mMinResultsForPopup;
   375   return NS_OK;
   376 }
   378 NS_IMETHODIMP nsFormFillController::SetMinResultsForPopup(uint32_t aMinResultsForPopup)
   379 {
   380   mMinResultsForPopup = aMinResultsForPopup;
   381   return NS_OK;
   382 }
   384 NS_IMETHODIMP
   385 nsFormFillController::GetMaxRows(uint32_t *aMaxRows)
   386 {
   387   *aMaxRows = mMaxRows;
   388   return NS_OK;
   389 }
   391 NS_IMETHODIMP
   392 nsFormFillController::SetMaxRows(uint32_t aMaxRows)
   393 {
   394   mMaxRows = aMaxRows;
   395   return NS_OK;
   396 }
   398 NS_IMETHODIMP
   399 nsFormFillController::GetShowImageColumn(bool *aShowImageColumn)
   400 {
   401   *aShowImageColumn = false;
   402   return NS_OK;
   403 }
   405 NS_IMETHODIMP nsFormFillController::SetShowImageColumn(bool aShowImageColumn)
   406 {
   407   return NS_ERROR_NOT_IMPLEMENTED;
   408 }
   411 NS_IMETHODIMP
   412 nsFormFillController::GetShowCommentColumn(bool *aShowCommentColumn)
   413 {
   414   *aShowCommentColumn = false;
   415   return NS_OK;
   416 }
   418 NS_IMETHODIMP nsFormFillController::SetShowCommentColumn(bool aShowCommentColumn)
   419 {
   420   return NS_ERROR_NOT_IMPLEMENTED;
   421 }
   423 NS_IMETHODIMP
   424 nsFormFillController::GetTimeout(uint32_t *aTimeout)
   425 {
   426   *aTimeout = mTimeout;
   427   return NS_OK;
   428 }
   430 NS_IMETHODIMP nsFormFillController::SetTimeout(uint32_t aTimeout)
   431 {
   432   mTimeout = aTimeout;
   433   return NS_OK;
   434 }
   436 NS_IMETHODIMP
   437 nsFormFillController::SetSearchParam(const nsAString &aSearchParam)
   438 {
   439   return NS_ERROR_NOT_IMPLEMENTED;
   440 }
   442 NS_IMETHODIMP
   443 nsFormFillController::GetSearchParam(nsAString &aSearchParam)
   444 {
   445   if (!mFocusedInput) {
   446     NS_WARNING("mFocusedInput is null for some reason! avoiding a crash. should find out why... - ben");
   447     return NS_ERROR_FAILURE; // XXX why? fix me.
   448   }
   450   mFocusedInput->GetName(aSearchParam);
   451   if (aSearchParam.IsEmpty()) {
   452     nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(mFocusedInput);
   453     element->GetId(aSearchParam);
   454   }
   456   return NS_OK;
   457 }
   459 NS_IMETHODIMP
   460 nsFormFillController::GetSearchCount(uint32_t *aSearchCount)
   461 {
   462   *aSearchCount = 1;
   463   return NS_OK;
   464 }
   466 NS_IMETHODIMP
   467 nsFormFillController::GetSearchAt(uint32_t index, nsACString & _retval)
   468 {
   469   _retval.Assign("form-history");
   470   return NS_OK;
   471 }
   473 NS_IMETHODIMP
   474 nsFormFillController::GetTextValue(nsAString & aTextValue)
   475 {
   476   if (mFocusedInput) {
   477     mFocusedInput->GetValue(aTextValue);
   478   } else {
   479     aTextValue.Truncate();
   480   }
   481   return NS_OK;
   482 }
   484 NS_IMETHODIMP
   485 nsFormFillController::SetTextValue(const nsAString & aTextValue)
   486 {
   487   nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(mFocusedInput);
   488   if (editable) {
   489     mSuppressOnInput = true;
   490     editable->SetUserInput(aTextValue);
   491     mSuppressOnInput = false;
   492   }
   493   return NS_OK;
   494 }
   496 NS_IMETHODIMP
   497 nsFormFillController::GetSelectionStart(int32_t *aSelectionStart)
   498 {
   499   if (mFocusedInput)
   500     mFocusedInput->GetSelectionStart(aSelectionStart);
   501   return NS_OK;
   502 }
   504 NS_IMETHODIMP
   505 nsFormFillController::GetSelectionEnd(int32_t *aSelectionEnd)
   506 {
   507   if (mFocusedInput)
   508     mFocusedInput->GetSelectionEnd(aSelectionEnd);
   509   return NS_OK;
   510 }
   512 NS_IMETHODIMP
   513 nsFormFillController::SelectTextRange(int32_t aStartIndex, int32_t aEndIndex)
   514 {
   515  if (mFocusedInput)
   516     mFocusedInput->SetSelectionRange(aStartIndex, aEndIndex, EmptyString());
   517   return NS_OK;
   518 }
   520 NS_IMETHODIMP
   521 nsFormFillController::OnSearchBegin()
   522 {
   523   return NS_OK;
   524 }
   526 NS_IMETHODIMP
   527 nsFormFillController::OnSearchComplete()
   528 {
   529   return NS_OK;
   530 }
   532 NS_IMETHODIMP
   533 nsFormFillController::OnTextEntered(bool* aPrevent)
   534 {
   535   NS_ENSURE_ARG(aPrevent);
   536   NS_ENSURE_TRUE(mFocusedInput, NS_OK);
   537   // Fire off a DOMAutoComplete event
   538   nsCOMPtr<nsIDOMDocument> domDoc;
   539   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mFocusedInput);
   540   element->GetOwnerDocument(getter_AddRefs(domDoc));
   541   NS_ENSURE_STATE(domDoc);
   543   nsCOMPtr<nsIDOMEvent> event;
   544   domDoc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
   545   NS_ENSURE_STATE(event);
   547   event->InitEvent(NS_LITERAL_STRING("DOMAutoComplete"), true, true);
   549   // XXXjst: We mark this event as a trusted event, it's up to the
   550   // callers of this to ensure that it's only called from trusted
   551   // code.
   552   event->SetTrusted(true);
   554   nsCOMPtr<EventTarget> targ = do_QueryInterface(mFocusedInput);
   556   bool defaultActionEnabled;
   557   targ->DispatchEvent(event, &defaultActionEnabled);
   558   *aPrevent = !defaultActionEnabled;
   559   return NS_OK;
   560 }
   562 NS_IMETHODIMP
   563 nsFormFillController::OnTextReverted(bool *_retval)
   564 {
   565   return NS_OK;
   566 }
   568 NS_IMETHODIMP
   569 nsFormFillController::GetConsumeRollupEvent(bool *aConsumeRollupEvent)
   570 {
   571   *aConsumeRollupEvent = false;
   572   return NS_OK;
   573 }
   575 NS_IMETHODIMP
   576 nsFormFillController::GetInPrivateContext(bool *aInPrivateContext)
   577 {
   578   if (!mFocusedInput) {
   579     *aInPrivateContext = false;
   580     return NS_OK;
   581   }
   583   nsCOMPtr<nsIDOMDocument> inputDoc;
   584   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mFocusedInput);
   585   element->GetOwnerDocument(getter_AddRefs(inputDoc));
   586   nsCOMPtr<nsIDocument> doc = do_QueryInterface(inputDoc);
   587   nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
   588   nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
   589   *aInPrivateContext = loadContext && loadContext->UsePrivateBrowsing();
   590   return NS_OK;
   591 }
   594 ////////////////////////////////////////////////////////////////////////
   595 //// nsIAutoCompleteSearch
   597 NS_IMETHODIMP
   598 nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAString &aSearchParam,
   599                                   nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
   600 {
   601   nsresult rv;
   602   nsCOMPtr<nsIAutoCompleteResult> result;
   604   // If the login manager has indicated it's responsible for this field, let it
   605   // handle the autocomplete. Otherwise, handle with form history.
   606   bool dummy;
   607   if (mPwmgrInputs.Get(mFocusedInputNode, &dummy)) {
   608     // XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
   609     // satchel manage the field?
   610     rv = mLoginManager->AutoCompleteSearch(aSearchString,
   611                                            aPreviousResult,
   612                                            mFocusedInput,
   613                                            getter_AddRefs(result));
   614     NS_ENSURE_SUCCESS(rv, rv);
   615     if (aListener) {
   616       aListener->OnSearchResult(this, result);
   617     }
   618   } else {
   619     mLastListener = aListener;
   621     // It appears that mFocusedInput is always null when we are focusing a XUL
   622     // element. Scary :)
   623     if (!mFocusedInput || nsContentUtils::IsAutocompleteEnabled(mFocusedInput)) {
   624       nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
   625         do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
   626       NS_ENSURE_SUCCESS(rv, rv);
   628       formAutoComplete->AutoCompleteSearchAsync(aSearchParam,
   629                                                 aSearchString,
   630                                                 mFocusedInput,
   631                                                 aPreviousResult,
   632                                                 this);
   633       mLastFormAutoComplete = formAutoComplete;
   634     } else {
   635       mLastSearchString = aSearchString;
   637       // Even if autocomplete is disabled, handle the inputlist anyway as that was
   638       // specifically requested by the page. This is so a field can have the default
   639       // autocomplete disabled and replaced with a custom inputlist autocomplete.
   640       return PerformInputListAutoComplete(aPreviousResult);
   641     }
   642   }
   644   return NS_OK;
   645 }
   647 nsresult
   648 nsFormFillController::PerformInputListAutoComplete(nsIAutoCompleteResult* aPreviousResult)
   649 {
   650   // If an <input> is focused, check if it has a list="<datalist>" which can
   651   // provide the list of suggestions.
   653   nsresult rv;
   654   nsCOMPtr<nsIAutoCompleteResult> result;
   656   nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
   657     do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
   658   NS_ENSURE_SUCCESS(rv, rv);
   659   rv = inputListAutoComplete->AutoCompleteSearch(aPreviousResult,
   660                                                  mLastSearchString,
   661                                                  mFocusedInput,
   662                                                  getter_AddRefs(result));
   663   NS_ENSURE_SUCCESS(rv, rv);
   665   if (mFocusedInput) {
   666     nsCOMPtr<nsIDOMHTMLElement> list;
   667     mFocusedInput->GetList(getter_AddRefs(list));
   669     // Add a mutation observer to check for changes to the items in the <datalist>
   670     // and update the suggestions accordingly.
   671     nsCOMPtr<nsINode> node = do_QueryInterface(list);
   672     if (mListNode != node) {
   673       if (mListNode) {
   674         mListNode->RemoveMutationObserver(this);
   675         mListNode = nullptr;
   676       }
   677       if (node) {
   678         node->AddMutationObserverUnlessExists(this);
   679         mListNode = node;
   680       }
   681     }
   682   }
   684   if (mLastListener) {
   685     mLastListener->OnSearchResult(this, result);
   686   }
   688   return NS_OK;
   689 }
   691 class UpdateSearchResultRunnable : public nsRunnable
   692 {
   693 public:
   694   UpdateSearchResultRunnable(nsIAutoCompleteObserver* aObserver,
   695                              nsIAutoCompleteSearch* aSearch,
   696                              nsIAutoCompleteResult* aResult)
   697     : mObserver(aObserver)
   698     , mSearch(aSearch)
   699     , mResult(aResult)
   700   {}
   702   NS_IMETHOD Run() {
   703     NS_ASSERTION(mObserver, "You shouldn't call this runnable with a null observer!");
   705     mObserver->OnUpdateSearchResult(mSearch, mResult);
   706     return NS_OK;
   707   }
   709 private:
   710   nsCOMPtr<nsIAutoCompleteObserver> mObserver;
   711   nsCOMPtr<nsIAutoCompleteSearch> mSearch;
   712   nsCOMPtr<nsIAutoCompleteResult> mResult;
   713 };
   715 void nsFormFillController::RevalidateDataList()
   716 {
   717   if (!mLastListener) {
   718     return;
   719   }
   720   nsresult rv;
   721   nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
   722     do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
   724   nsCOMPtr<nsIAutoCompleteResult> result;
   726   rv = inputListAutoComplete->AutoCompleteSearch(mLastSearchResult,
   727                                                  mLastSearchString,
   728                                                  mFocusedInput,
   729                                                  getter_AddRefs(result));
   731   nsCOMPtr<nsIRunnable> event =
   732     new UpdateSearchResultRunnable(mLastListener, this, result);
   733   NS_DispatchToCurrentThread(event);
   734 }
   736 NS_IMETHODIMP
   737 nsFormFillController::StopSearch()
   738 {
   739   // Make sure to stop and clear this, otherwise the controller will prevent
   740   // mLastFormAutoComplete from being deleted.
   741   if (mLastFormAutoComplete) {
   742     mLastFormAutoComplete->StopAutoCompleteSearch();
   743     mLastFormAutoComplete = nullptr;
   744   }
   745   return NS_OK;
   746 }
   748 ////////////////////////////////////////////////////////////////////////
   749 //// nsIFormAutoCompleteObserver
   751 NS_IMETHODIMP
   752 nsFormFillController::OnSearchCompletion(nsIAutoCompleteResult *aResult)
   753 {
   754   nsCOMPtr<nsIAutoCompleteResult> resultParam = do_QueryInterface(aResult);
   756   nsAutoString searchString;
   757   resultParam->GetSearchString(searchString);
   758   mLastSearchResult = aResult;
   759   mLastSearchString = searchString;
   761   return PerformInputListAutoComplete(resultParam);
   762 }
   764 ////////////////////////////////////////////////////////////////////////
   765 //// nsIDOMEventListener
   767 NS_IMETHODIMP
   768 nsFormFillController::HandleEvent(nsIDOMEvent* aEvent)
   769 {
   770   nsAutoString type;
   771   aEvent->GetType(type);
   773   if (type.EqualsLiteral("focus")) {
   774     return Focus(aEvent);
   775   }
   776   if (type.EqualsLiteral("mousedown")) {
   777     return MouseDown(aEvent);
   778   }
   779   if (type.EqualsLiteral("keypress")) {
   780     return KeyPress(aEvent);
   781   }
   782   if (type.EqualsLiteral("input")) {
   783     return (!mSuppressOnInput && mController && mFocusedInput) ?
   784            mController->HandleText() : NS_OK;
   785   }
   786   if (type.EqualsLiteral("blur")) {
   787     if (mFocusedInput)
   788       StopControllingInput();
   789     return NS_OK;
   790   }
   791   if (type.EqualsLiteral("compositionstart")) {
   792     NS_ASSERTION(mController, "should have a controller!");
   793     if (mController && mFocusedInput)
   794       mController->HandleStartComposition();
   795     return NS_OK;
   796   }
   797   if (type.EqualsLiteral("compositionend")) {
   798     NS_ASSERTION(mController, "should have a controller!");
   799     if (mController && mFocusedInput)
   800       mController->HandleEndComposition();
   801     return NS_OK;
   802   }
   803   if (type.EqualsLiteral("contextmenu")) {
   804     if (mFocusedPopup)
   805       mFocusedPopup->ClosePopup();
   806     return NS_OK;
   807   }
   808   if (type.EqualsLiteral("pagehide")) {
   810     nsCOMPtr<nsIDocument> doc = do_QueryInterface(
   811       aEvent->InternalDOMEvent()->GetTarget());
   812     if (!doc)
   813       return NS_OK;
   815     if (mFocusedInput) {
   816       if (doc == mFocusedInputNode->OwnerDoc())
   817         StopControllingInput();
   818     }
   820     PwmgrInputsEnumData ed(this, doc);
   821     mPwmgrInputs.Enumerate(RemoveForDocumentEnumerator, &ed);
   822   }
   824   return NS_OK;
   825 }
   828 /* static */ PLDHashOperator
   829 nsFormFillController::RemoveForDocumentEnumerator(const nsINode* aKey,
   830                                                   bool& aEntry,
   831                                                   void* aUserData)
   832 {
   833   PwmgrInputsEnumData* ed = static_cast<PwmgrInputsEnumData*>(aUserData);
   834   if (aKey && (!ed->mDoc || aKey->OwnerDoc() == ed->mDoc)) {
   835     // mFocusedInputNode's observer is tracked separately, don't remove it here.
   836     if (aKey != ed->mFFC->mFocusedInputNode) {
   837       const_cast<nsINode*>(aKey)->RemoveMutationObserver(ed->mFFC);
   838     }
   839     return PL_DHASH_REMOVE;
   840   }
   841   return PL_DHASH_NEXT;
   842 }
   844 nsresult
   845 nsFormFillController::Focus(nsIDOMEvent* aEvent)
   846 {
   847   nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(
   848     aEvent->InternalDOMEvent()->GetTarget());
   849   nsCOMPtr<nsINode> inputNode = do_QueryInterface(input);
   850   if (!inputNode)
   851     return NS_OK;
   853   nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(input);
   854   if (!formControl || !formControl->IsSingleLineTextControl(true))
   855     return NS_OK;
   857   bool isReadOnly = false;
   858   input->GetReadOnly(&isReadOnly);
   859   if (isReadOnly)
   860     return NS_OK;
   862   bool autocomplete = nsContentUtils::IsAutocompleteEnabled(input);
   864   nsCOMPtr<nsIDOMHTMLElement> datalist;
   865   input->GetList(getter_AddRefs(datalist));
   866   bool hasList = datalist != nullptr;
   868   bool dummy;
   869   bool isPwmgrInput = false;
   870   if (mPwmgrInputs.Get(inputNode, &dummy))
   871       isPwmgrInput = true;
   873   if (isPwmgrInput || hasList || autocomplete) {
   874     StartControllingInput(input);
   875   }
   877   return NS_OK;
   878 }
   880 nsresult
   881 nsFormFillController::KeyPress(nsIDOMEvent* aEvent)
   882 {
   883   NS_ASSERTION(mController, "should have a controller!");
   884   if (!mFocusedInput || !mController)
   885     return NS_OK;
   887   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
   888   if (!keyEvent)
   889     return NS_ERROR_FAILURE;
   891   bool cancel = false;
   893   uint32_t k;
   894   keyEvent->GetKeyCode(&k);
   895   switch (k) {
   896   case nsIDOMKeyEvent::DOM_VK_DELETE:
   897 #ifndef XP_MACOSX
   898     mController->HandleDelete(&cancel);
   899     break;
   900   case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
   901     mController->HandleText();
   902     break;
   903 #else
   904   case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
   905     {
   906       bool isShift = false;
   907       keyEvent->GetShiftKey(&isShift);
   909       if (isShift)
   910         mController->HandleDelete(&cancel);
   911       else
   912         mController->HandleText();
   914       break;
   915     }
   916 #endif
   917   case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
   918   case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
   919     {
   920       bool isCtrl, isAlt, isMeta;
   921       keyEvent->GetCtrlKey(&isCtrl);
   922       keyEvent->GetAltKey(&isAlt);
   923       keyEvent->GetMetaKey(&isMeta);
   924       if (isCtrl || isAlt || isMeta)
   925         break;
   926     }
   927     /* fall through */
   928   case nsIDOMKeyEvent::DOM_VK_UP:
   929   case nsIDOMKeyEvent::DOM_VK_DOWN:
   930   case nsIDOMKeyEvent::DOM_VK_LEFT:
   931   case nsIDOMKeyEvent::DOM_VK_RIGHT:
   932     mController->HandleKeyNavigation(k, &cancel);
   933     break;
   934   case nsIDOMKeyEvent::DOM_VK_ESCAPE:
   935     mController->HandleEscape(&cancel);
   936     break;
   937   case nsIDOMKeyEvent::DOM_VK_TAB:
   938     mController->HandleTab();
   939     cancel = false;
   940     break;
   941   case nsIDOMKeyEvent::DOM_VK_RETURN:
   942     mController->HandleEnter(false, &cancel);
   943     break;
   944   }
   946   if (cancel) {
   947     aEvent->PreventDefault();
   948   }
   950   return NS_OK;
   951 }
   953 nsresult
   954 nsFormFillController::MouseDown(nsIDOMEvent* aEvent)
   955 {
   956   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aEvent));
   957   if (!mouseEvent)
   958     return NS_ERROR_FAILURE;
   960   nsCOMPtr<nsIDOMHTMLInputElement> targetInput = do_QueryInterface(
   961     aEvent->InternalDOMEvent()->GetTarget());
   962   if (!targetInput)
   963     return NS_OK;
   965   int16_t button;
   966   mouseEvent->GetButton(&button);
   967   if (button != 0)
   968     return NS_OK;
   970   bool isOpen = false;
   971   GetPopupOpen(&isOpen);
   972   if (isOpen)
   973     return NS_OK;
   975   nsCOMPtr<nsIAutoCompleteInput> input;
   976   mController->GetInput(getter_AddRefs(input));
   977   if (!input)
   978     return NS_OK;
   980   nsAutoString value;
   981   input->GetTextValue(value);
   982   if (value.Length() > 0) {
   983     // Show the popup with a filtered result set
   984     mController->SetSearchString(EmptyString());
   985     mController->HandleText();
   986   } else {
   987     // Show the popup with the complete result set.  Can't use HandleText()
   988     // because it doesn't display the popup if the input is blank.
   989     bool cancel = false;
   990     mController->HandleKeyNavigation(nsIDOMKeyEvent::DOM_VK_DOWN, &cancel);
   991   }
   993   return NS_OK;
   994 }
   996 ////////////////////////////////////////////////////////////////////////
   997 //// nsFormFillController
   999 void
  1000 nsFormFillController::AddWindowListeners(nsIDOMWindow *aWindow)
  1002   if (!aWindow)
  1003     return;
  1005   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aWindow));
  1006   EventTarget* target = nullptr;
  1007   if (privateDOMWindow)
  1008     target = privateDOMWindow->GetChromeEventHandler();
  1010   if (!target)
  1011     return;
  1013   target->AddEventListener(NS_LITERAL_STRING("focus"), this,
  1014                            true, false);
  1015   target->AddEventListener(NS_LITERAL_STRING("blur"), this,
  1016                            true, false);
  1017   target->AddEventListener(NS_LITERAL_STRING("pagehide"), this,
  1018                            true, false);
  1019   target->AddEventListener(NS_LITERAL_STRING("mousedown"), this,
  1020                            true, false);
  1021   target->AddEventListener(NS_LITERAL_STRING("input"), this,
  1022                            true, false);
  1023   target->AddEventListener(NS_LITERAL_STRING("compositionstart"), this,
  1024                            true, false);
  1025   target->AddEventListener(NS_LITERAL_STRING("compositionend"), this,
  1026                            true, false);
  1027   target->AddEventListener(NS_LITERAL_STRING("contextmenu"), this,
  1028                            true, false);
  1030   // Note that any additional listeners added should ensure that they ignore
  1031   // untrusted events, which might be sent by content that's up to no good.
  1034 void
  1035 nsFormFillController::RemoveWindowListeners(nsIDOMWindow *aWindow)
  1037   if (!aWindow)
  1038     return;
  1040   StopControllingInput();
  1042   nsCOMPtr<nsIDOMDocument> domDoc;
  1043   aWindow->GetDocument(getter_AddRefs(domDoc));
  1044   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
  1045   PwmgrInputsEnumData ed(this, doc);
  1046   mPwmgrInputs.Enumerate(RemoveForDocumentEnumerator, &ed);
  1048   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aWindow));
  1049   EventTarget* target = nullptr;
  1050   if (privateDOMWindow)
  1051     target = privateDOMWindow->GetChromeEventHandler();
  1053   if (!target)
  1054     return;
  1056   target->RemoveEventListener(NS_LITERAL_STRING("focus"), this, true);
  1057   target->RemoveEventListener(NS_LITERAL_STRING("blur"), this, true);
  1058   target->RemoveEventListener(NS_LITERAL_STRING("pagehide"), this, true);
  1059   target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true);
  1060   target->RemoveEventListener(NS_LITERAL_STRING("input"), this, true);
  1061   target->RemoveEventListener(NS_LITERAL_STRING("compositionstart"), this,
  1062                               true);
  1063   target->RemoveEventListener(NS_LITERAL_STRING("compositionend"), this,
  1064                               true);
  1065   target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this, true);
  1068 void
  1069 nsFormFillController::AddKeyListener(nsINode* aInput)
  1071   aInput->AddEventListener(NS_LITERAL_STRING("keypress"), this,
  1072                            true, false);
  1075 void
  1076 nsFormFillController::RemoveKeyListener()
  1078   if (!mFocusedInputNode)
  1079     return;
  1081   mFocusedInputNode->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
  1084 void
  1085 nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput)
  1087   // Make sure we're not still attached to an input
  1088   StopControllingInput();
  1090   // Find the currently focused docShell
  1091   nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(aInput);
  1092   int32_t index = GetIndexOfDocShell(docShell);
  1093   if (index < 0)
  1094     return;
  1096   // Cache the popup for the focused docShell
  1097   mFocusedPopup = mPopups.SafeElementAt(index);
  1099   nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
  1100   if (!node) {
  1101     return;
  1104   AddKeyListener(node);
  1106   node->AddMutationObserverUnlessExists(this);
  1107   mFocusedInputNode = node;
  1108   mFocusedInput = aInput;
  1110   nsCOMPtr<nsIDOMHTMLElement> list;
  1111   mFocusedInput->GetList(getter_AddRefs(list));
  1112   nsCOMPtr<nsINode> listNode = do_QueryInterface(list);
  1113   if (listNode) {
  1114     listNode->AddMutationObserverUnlessExists(this);
  1115     mListNode = listNode;
  1118   // Now we are the autocomplete controller's bitch
  1119   mController->SetInput(this);
  1122 void
  1123 nsFormFillController::StopControllingInput()
  1125   RemoveKeyListener();
  1127   if (mListNode) {
  1128     mListNode->RemoveMutationObserver(this);
  1129     mListNode = nullptr;
  1132   // Reset the controller's input, but not if it has been switched
  1133   // to another input already, which might happen if the user switches
  1134   // focus by clicking another autocomplete textbox
  1135   nsCOMPtr<nsIAutoCompleteInput> input;
  1136   mController->GetInput(getter_AddRefs(input));
  1137   if (input == this)
  1138     mController->SetInput(nullptr);
  1140   if (mFocusedInputNode) {
  1141     MaybeRemoveMutationObserver(mFocusedInputNode);
  1142     mFocusedInputNode = nullptr;
  1143     mFocusedInput = nullptr;
  1145   mFocusedPopup = nullptr;
  1148 nsIDocShell *
  1149 nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
  1151   nsCOMPtr<nsIDOMDocument> domDoc;
  1152   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aInput);
  1153   element->GetOwnerDocument(getter_AddRefs(domDoc));
  1154   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
  1155   NS_ENSURE_TRUE(doc, nullptr);
  1156   nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(doc->GetWindow());
  1157   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
  1158   return docShell;
  1161 nsIDOMWindow *
  1162 nsFormFillController::GetWindowForDocShell(nsIDocShell *aDocShell)
  1164   nsCOMPtr<nsIContentViewer> contentViewer;
  1165   aDocShell->GetContentViewer(getter_AddRefs(contentViewer));
  1166   NS_ENSURE_TRUE(contentViewer, nullptr);
  1168   nsCOMPtr<nsIDOMDocument> domDoc;
  1169   contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
  1170   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
  1171   NS_ENSURE_TRUE(doc, nullptr);
  1173   return doc->GetWindow();
  1176 int32_t
  1177 nsFormFillController::GetIndexOfDocShell(nsIDocShell *aDocShell)
  1179   if (!aDocShell)
  1180     return -1;
  1182   // Loop through our cached docShells looking for the given docShell
  1183   uint32_t count = mDocShells.Length();
  1184   for (uint32_t i = 0; i < count; ++i) {
  1185     if (mDocShells[i] == aDocShell)
  1186       return i;
  1189   // Recursively check the parent docShell of this one
  1190   nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(aDocShell);
  1191   nsCOMPtr<nsIDocShellTreeItem> parentItem;
  1192   treeItem->GetParent(getter_AddRefs(parentItem));
  1193   if (parentItem) {
  1194     nsCOMPtr<nsIDocShell> parentShell = do_QueryInterface(parentItem);
  1195     return GetIndexOfDocShell(parentShell);
  1198   return -1;
  1201 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormFillController)
  1203 NS_DEFINE_NAMED_CID(NS_FORMFILLCONTROLLER_CID);
  1205 static const mozilla::Module::CIDEntry kSatchelCIDs[] = {
  1206   { &kNS_FORMFILLCONTROLLER_CID, false, nullptr, nsFormFillControllerConstructor },
  1207   { nullptr }
  1208 };
  1210 static const mozilla::Module::ContractIDEntry kSatchelContracts[] = {
  1211   { "@mozilla.org/satchel/form-fill-controller;1", &kNS_FORMFILLCONTROLLER_CID },
  1212   { NS_FORMHISTORYAUTOCOMPLETE_CONTRACTID, &kNS_FORMFILLCONTROLLER_CID },
  1213   { nullptr }
  1214 };
  1216 static const mozilla::Module kSatchelModule = {
  1217   mozilla::Module::kVersion,
  1218   kSatchelCIDs,
  1219   kSatchelContracts
  1220 };
  1222 NSMODULE_DEFN(satchel) = &kSatchelModule;

mercurial