Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsFormFillController.h"
8 :
9 : #include "mozilla/ClearOnShutdown.h"
10 : #include "mozilla/ErrorResult.h"
11 : #include "mozilla/dom/Element.h"
12 : #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
13 : #include "mozilla/dom/HTMLInputElement.h"
14 : #include "nsIFormAutoComplete.h"
15 : #include "nsIInputListAutoComplete.h"
16 : #include "nsIAutoCompleteSimpleResult.h"
17 : #include "nsString.h"
18 : #include "nsReadableUtils.h"
19 : #include "nsIServiceManager.h"
20 : #include "nsIInterfaceRequestor.h"
21 : #include "nsIInterfaceRequestorUtils.h"
22 : #include "nsIDocShellTreeItem.h"
23 : #include "nsPIDOMWindow.h"
24 : #include "nsIWebNavigation.h"
25 : #include "nsIContentViewer.h"
26 : #include "nsIDOMKeyEvent.h"
27 : #include "nsIDOMDocument.h"
28 : #include "nsIDOMElement.h"
29 : #include "nsIDocument.h"
30 : #include "nsIContent.h"
31 : #include "nsIPresShell.h"
32 : #include "nsRect.h"
33 : #include "nsIDOMHTMLFormElement.h"
34 : #include "nsILoginManager.h"
35 : #include "nsIDOMMouseEvent.h"
36 : #include "mozilla/ModuleUtils.h"
37 : #include "nsToolkitCompsCID.h"
38 : #include "nsEmbedCID.h"
39 : #include "nsIDOMNSEditableElement.h"
40 : #include "nsContentUtils.h"
41 : #include "nsILoadContext.h"
42 : #include "nsIFrame.h"
43 : #include "nsIScriptSecurityManager.h"
44 : #include "nsFocusManager.h"
45 :
46 : using namespace mozilla;
47 : using namespace mozilla::dom;
48 : using mozilla::ErrorResult;
49 :
50 : static nsIFormAutoComplete*
51 0 : GetFormAutoComplete()
52 : {
53 0 : static nsCOMPtr<nsIFormAutoComplete> sInstance;
54 : static bool sInitialized = false;
55 0 : if (!sInitialized) {
56 : nsresult rv;
57 : sInstance =
58 0 : do_GetService("@mozilla.org/satchel/form-autocomplete;1",
59 0 : &rv);
60 :
61 0 : if (NS_SUCCEEDED(rv)) {
62 0 : ClearOnShutdown(&sInstance);
63 0 : sInitialized = true;
64 : }
65 : }
66 0 : return sInstance;
67 : }
68 :
69 0 : NS_IMPL_CYCLE_COLLECTION(nsFormFillController,
70 : mController, mLoginManager, mFocusedPopup, mDocShells,
71 : mPopups, mLastListener, mLastFormAutoComplete)
72 :
73 52 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFormFillController)
74 49 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFormFillController)
75 41 : NS_INTERFACE_MAP_ENTRY(nsIFormFillController)
76 33 : NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteInput)
77 33 : NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteSearch)
78 33 : NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
79 33 : NS_INTERFACE_MAP_ENTRY(nsIFormAutoCompleteObserver)
80 33 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
81 33 : NS_INTERFACE_MAP_END
82 :
83 40 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFormFillController)
84 20 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFormFillController)
85 :
86 :
87 :
88 3 : nsFormFillController::nsFormFillController() :
89 : mFocusedInput(nullptr),
90 : mFocusedInputNode(nullptr),
91 : mListNode(nullptr),
92 : // The amount of time a context menu event supresses showing a
93 : // popup from a focus event in ms. This matches the threshold in
94 : // toolkit/components/passwordmgr/LoginManagerContent.jsm.
95 : mFocusAfterRightClickThreshold(400),
96 : mTimeout(50),
97 : mMinResultsForPopup(1),
98 : mMaxRows(0),
99 : mLastRightClickTimeStamp(TimeStamp()),
100 : mDisableAutoComplete(false),
101 : mCompleteDefaultIndex(false),
102 : mCompleteSelectedIndex(false),
103 : mForceComplete(false),
104 3 : mSuppressOnInput(false)
105 : {
106 3 : mController = do_GetService("@mozilla.org/autocomplete/controller;1");
107 3 : MOZ_ASSERT(mController);
108 3 : }
109 :
110 0 : nsFormFillController::~nsFormFillController()
111 : {
112 0 : if (mListNode) {
113 0 : mListNode->RemoveMutationObserver(this);
114 0 : mListNode = nullptr;
115 : }
116 0 : if (mFocusedInputNode) {
117 0 : MaybeRemoveMutationObserver(mFocusedInputNode);
118 0 : mFocusedInputNode = nullptr;
119 0 : mFocusedInput = nullptr;
120 : }
121 0 : RemoveForDocument(nullptr);
122 :
123 : // Remove ourselves as a focus listener from all cached docShells
124 0 : uint32_t count = mDocShells.Length();
125 0 : for (uint32_t i = 0; i < count; ++i) {
126 0 : nsCOMPtr<nsPIDOMWindowOuter> window = GetWindowForDocShell(mDocShells[i]);
127 0 : RemoveWindowListeners(window);
128 : }
129 0 : }
130 :
131 : ////////////////////////////////////////////////////////////////////////
132 : //// nsIMutationObserver
133 : //
134 :
135 : void
136 0 : nsFormFillController::AttributeChanged(nsIDocument* aDocument,
137 : mozilla::dom::Element* aElement,
138 : int32_t aNameSpaceID,
139 : nsIAtom* aAttribute, int32_t aModType,
140 : const nsAttrValue* aOldValue)
141 : {
142 0 : if ((aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::readonly ||
143 0 : aAttribute == nsGkAtoms::autocomplete) &&
144 : aNameSpaceID == kNameSpaceID_None) {
145 0 : nsCOMPtr<nsIDOMHTMLInputElement> focusedInput(mFocusedInput);
146 : // Reset the current state of the controller, unconditionally.
147 0 : StopControllingInput();
148 : // Then restart based on the new values. We have to delay this
149 : // to avoid ending up in an endless loop due to re-registering our
150 : // mutation observer (which would notify us again for *this* event).
151 : nsCOMPtr<nsIRunnable> event =
152 0 : mozilla::NewRunnableMethod<nsCOMPtr<nsIDOMHTMLInputElement>>(
153 : "nsFormFillController::MaybeStartControllingInput",
154 : this,
155 : &nsFormFillController::MaybeStartControllingInput,
156 0 : focusedInput);
157 0 : NS_DispatchToCurrentThread(event);
158 : }
159 :
160 0 : if (mListNode && mListNode->Contains(aElement)) {
161 0 : RevalidateDataList();
162 : }
163 0 : }
164 :
165 : void
166 0 : nsFormFillController::ContentAppended(nsIDocument* aDocument,
167 : nsIContent* aContainer,
168 : nsIContent* aChild,
169 : int32_t aIndexInContainer)
170 : {
171 0 : if (mListNode && mListNode->Contains(aContainer)) {
172 0 : RevalidateDataList();
173 : }
174 0 : }
175 :
176 : void
177 0 : nsFormFillController::ContentInserted(nsIDocument* aDocument,
178 : nsIContent* aContainer,
179 : nsIContent* aChild,
180 : int32_t aIndexInContainer)
181 : {
182 0 : if (mListNode && mListNode->Contains(aContainer)) {
183 0 : RevalidateDataList();
184 : }
185 0 : }
186 :
187 : void
188 0 : nsFormFillController::ContentRemoved(nsIDocument* aDocument,
189 : nsIContent* aContainer,
190 : nsIContent* aChild,
191 : int32_t aIndexInContainer,
192 : nsIContent* aPreviousSibling)
193 : {
194 0 : if (mListNode && mListNode->Contains(aContainer)) {
195 0 : RevalidateDataList();
196 : }
197 0 : }
198 :
199 : void
200 0 : nsFormFillController::CharacterDataWillChange(nsIDocument* aDocument,
201 : nsIContent* aContent,
202 : CharacterDataChangeInfo* aInfo)
203 : {
204 0 : }
205 :
206 : void
207 0 : nsFormFillController::CharacterDataChanged(nsIDocument* aDocument,
208 : nsIContent* aContent,
209 : CharacterDataChangeInfo* aInfo)
210 : {
211 0 : }
212 :
213 : void
214 0 : nsFormFillController::AttributeWillChange(nsIDocument* aDocument,
215 : mozilla::dom::Element* aElement,
216 : int32_t aNameSpaceID,
217 : nsIAtom* aAttribute, int32_t aModType,
218 : const nsAttrValue* aNewValue)
219 : {
220 0 : }
221 :
222 : void
223 0 : nsFormFillController::NativeAnonymousChildListChange(nsIDocument* aDocument,
224 : nsIContent* aContent,
225 : bool aIsRemove)
226 : {
227 0 : }
228 :
229 : void
230 0 : nsFormFillController::ParentChainChanged(nsIContent* aContent)
231 : {
232 0 : }
233 :
234 : void
235 0 : nsFormFillController::NodeWillBeDestroyed(const nsINode* aNode)
236 : {
237 0 : mPwmgrInputs.Remove(aNode);
238 0 : mAutofillInputs.Remove(aNode);
239 0 : if (aNode == mListNode) {
240 0 : mListNode = nullptr;
241 0 : RevalidateDataList();
242 0 : } else if (aNode == mFocusedInputNode) {
243 0 : mFocusedInputNode = nullptr;
244 0 : mFocusedInput = nullptr;
245 : }
246 0 : }
247 :
248 : void
249 0 : nsFormFillController::MaybeRemoveMutationObserver(nsINode* aNode)
250 : {
251 : // Nodes being tracked in mPwmgrInputs will have their observers removed when
252 : // they stop being tracked.
253 0 : if (!mPwmgrInputs.Get(aNode) && !mAutofillInputs.Get(aNode)) {
254 0 : aNode->RemoveMutationObserver(this);
255 : }
256 0 : }
257 :
258 : ////////////////////////////////////////////////////////////////////////
259 : //// nsIFormFillController
260 :
261 : NS_IMETHODIMP
262 1 : nsFormFillController::AttachToBrowser(nsIDocShell *aDocShell, nsIAutoCompletePopup *aPopup)
263 : {
264 1 : NS_ENSURE_TRUE(aDocShell && aPopup, NS_ERROR_ILLEGAL_VALUE);
265 :
266 1 : mDocShells.AppendElement(aDocShell);
267 1 : mPopups.AppendElement(aPopup);
268 :
269 : // Listen for focus events on the domWindow of the docShell
270 2 : nsCOMPtr<nsPIDOMWindowOuter> window = GetWindowForDocShell(aDocShell);
271 1 : AddWindowListeners(window);
272 :
273 1 : return NS_OK;
274 : }
275 :
276 : NS_IMETHODIMP
277 0 : nsFormFillController::DetachFromBrowser(nsIDocShell *aDocShell)
278 : {
279 0 : int32_t index = GetIndexOfDocShell(aDocShell);
280 0 : NS_ENSURE_TRUE(index >= 0, NS_ERROR_FAILURE);
281 :
282 : // Stop listening for focus events on the domWindow of the docShell
283 : nsCOMPtr<nsPIDOMWindowOuter> window =
284 0 : GetWindowForDocShell(mDocShells.SafeElementAt(index));
285 0 : RemoveWindowListeners(window);
286 :
287 0 : mDocShells.RemoveElementAt(index);
288 0 : mPopups.RemoveElementAt(index);
289 :
290 0 : return NS_OK;
291 : }
292 :
293 :
294 : NS_IMETHODIMP
295 0 : nsFormFillController::MarkAsLoginManagerField(nsIDOMHTMLInputElement *aInput)
296 : {
297 : /*
298 : * The Login Manager can supply autocomplete results for username fields,
299 : * when a user has multiple logins stored for a site. It uses this
300 : * interface to indicate that the form manager shouldn't handle the
301 : * autocomplete. The form manager also checks for this tag when saving
302 : * form history (so it doesn't save usernames).
303 : */
304 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
305 0 : NS_ENSURE_STATE(node);
306 :
307 : // If the field was already marked, we don't want to show the popup again.
308 0 : if (mPwmgrInputs.Get(node)) {
309 0 : return NS_OK;
310 : }
311 :
312 0 : mPwmgrInputs.Put(node, true);
313 0 : node->AddMutationObserverUnlessExists(this);
314 :
315 0 : nsFocusManager *fm = nsFocusManager::GetFocusManager();
316 0 : if (fm) {
317 0 : nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
318 0 : if (SameCOMIdentity(focusedContent, node)) {
319 0 : nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(node);
320 0 : if (!mFocusedInput) {
321 0 : MaybeStartControllingInput(input);
322 : }
323 : }
324 : }
325 :
326 0 : if (!mLoginManager) {
327 0 : mLoginManager = do_GetService("@mozilla.org/login-manager;1");
328 : }
329 :
330 0 : return NS_OK;
331 : }
332 :
333 : NS_IMETHODIMP
334 0 : nsFormFillController::MarkAsAutofillField(nsIDOMHTMLInputElement *aInput)
335 : {
336 : /*
337 : * Support other components implementing form autofill and handle autocomplete
338 : * for the field.
339 : */
340 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
341 0 : NS_ENSURE_STATE(node);
342 :
343 0 : if (mAutofillInputs.Get(node)) {
344 0 : return NS_OK;
345 : }
346 :
347 0 : mAutofillInputs.Put(node, true);
348 0 : node->AddMutationObserverUnlessExists(this);
349 :
350 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(aInput);
351 0 : txtCtrl->EnablePreview();
352 :
353 0 : nsFocusManager *fm = nsFocusManager::GetFocusManager();
354 0 : if (fm) {
355 0 : nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
356 0 : if (SameCOMIdentity(focusedContent, node)) {
357 0 : nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(node);
358 0 : MaybeStartControllingInput(input);
359 : }
360 : }
361 :
362 0 : return NS_OK;
363 : }
364 :
365 : NS_IMETHODIMP
366 0 : nsFormFillController::GetFocusedInput(nsIDOMHTMLInputElement **aInput) {
367 0 : *aInput = mFocusedInput;
368 0 : NS_IF_ADDREF(*aInput);
369 0 : return NS_OK;
370 : }
371 :
372 : ////////////////////////////////////////////////////////////////////////
373 : //// nsIAutoCompleteInput
374 :
375 : NS_IMETHODIMP
376 0 : nsFormFillController::GetPopup(nsIAutoCompletePopup **aPopup)
377 : {
378 0 : *aPopup = mFocusedPopup;
379 0 : NS_IF_ADDREF(*aPopup);
380 0 : return NS_OK;
381 : }
382 :
383 : NS_IMETHODIMP
384 0 : nsFormFillController::GetController(nsIAutoCompleteController **aController)
385 : {
386 0 : *aController = mController;
387 0 : NS_IF_ADDREF(*aController);
388 0 : return NS_OK;
389 : }
390 :
391 : NS_IMETHODIMP
392 0 : nsFormFillController::GetPopupOpen(bool *aPopupOpen)
393 : {
394 0 : if (mFocusedPopup) {
395 0 : mFocusedPopup->GetPopupOpen(aPopupOpen);
396 : } else {
397 0 : *aPopupOpen = false;
398 : }
399 0 : return NS_OK;
400 : }
401 :
402 : NS_IMETHODIMP
403 0 : nsFormFillController::SetPopupOpen(bool aPopupOpen)
404 : {
405 0 : if (mFocusedPopup) {
406 0 : if (aPopupOpen) {
407 : // make sure input field is visible before showing popup (bug 320938)
408 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
409 0 : NS_ENSURE_STATE(content);
410 0 : nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(mFocusedInput);
411 0 : NS_ENSURE_STATE(docShell);
412 0 : nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
413 0 : NS_ENSURE_STATE(presShell);
414 0 : presShell->ScrollContentIntoView(content,
415 : nsIPresShell::ScrollAxis(
416 : nsIPresShell::SCROLL_MINIMUM,
417 : nsIPresShell::SCROLL_IF_NOT_VISIBLE),
418 : nsIPresShell::ScrollAxis(
419 : nsIPresShell::SCROLL_MINIMUM,
420 : nsIPresShell::SCROLL_IF_NOT_VISIBLE),
421 0 : nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
422 : // mFocusedPopup can be destroyed after ScrollContentIntoView, see bug 420089
423 0 : if (mFocusedPopup) {
424 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mFocusedInput);
425 0 : mFocusedPopup->OpenAutocompletePopup(this, element);
426 : }
427 : } else {
428 0 : mFocusedPopup->ClosePopup();
429 : }
430 : }
431 :
432 0 : return NS_OK;
433 : }
434 :
435 : NS_IMETHODIMP
436 0 : nsFormFillController::GetDisableAutoComplete(bool *aDisableAutoComplete)
437 : {
438 0 : *aDisableAutoComplete = mDisableAutoComplete;
439 0 : return NS_OK;
440 : }
441 :
442 : NS_IMETHODIMP
443 0 : nsFormFillController::SetDisableAutoComplete(bool aDisableAutoComplete)
444 : {
445 0 : mDisableAutoComplete = aDisableAutoComplete;
446 0 : return NS_OK;
447 : }
448 :
449 : NS_IMETHODIMP
450 0 : nsFormFillController::GetCompleteDefaultIndex(bool *aCompleteDefaultIndex)
451 : {
452 0 : *aCompleteDefaultIndex = mCompleteDefaultIndex;
453 0 : return NS_OK;
454 : }
455 :
456 : NS_IMETHODIMP
457 0 : nsFormFillController::SetCompleteDefaultIndex(bool aCompleteDefaultIndex)
458 : {
459 0 : mCompleteDefaultIndex = aCompleteDefaultIndex;
460 0 : return NS_OK;
461 : }
462 :
463 : NS_IMETHODIMP
464 0 : nsFormFillController::GetCompleteSelectedIndex(bool *aCompleteSelectedIndex)
465 : {
466 0 : *aCompleteSelectedIndex = mCompleteSelectedIndex;
467 0 : return NS_OK;
468 : }
469 :
470 : NS_IMETHODIMP
471 0 : nsFormFillController::SetCompleteSelectedIndex(bool aCompleteSelectedIndex)
472 : {
473 0 : mCompleteSelectedIndex = aCompleteSelectedIndex;
474 0 : return NS_OK;
475 : }
476 :
477 : NS_IMETHODIMP
478 0 : nsFormFillController::GetForceComplete(bool *aForceComplete)
479 : {
480 0 : *aForceComplete = mForceComplete;
481 0 : return NS_OK;
482 : }
483 :
484 0 : NS_IMETHODIMP nsFormFillController::SetForceComplete(bool aForceComplete)
485 : {
486 0 : mForceComplete = aForceComplete;
487 0 : return NS_OK;
488 : }
489 :
490 : NS_IMETHODIMP
491 0 : nsFormFillController::GetMinResultsForPopup(uint32_t *aMinResultsForPopup)
492 : {
493 0 : *aMinResultsForPopup = mMinResultsForPopup;
494 0 : return NS_OK;
495 : }
496 :
497 0 : NS_IMETHODIMP nsFormFillController::SetMinResultsForPopup(uint32_t aMinResultsForPopup)
498 : {
499 0 : mMinResultsForPopup = aMinResultsForPopup;
500 0 : return NS_OK;
501 : }
502 :
503 : NS_IMETHODIMP
504 0 : nsFormFillController::GetMaxRows(uint32_t *aMaxRows)
505 : {
506 0 : *aMaxRows = mMaxRows;
507 0 : return NS_OK;
508 : }
509 :
510 : NS_IMETHODIMP
511 0 : nsFormFillController::SetMaxRows(uint32_t aMaxRows)
512 : {
513 0 : mMaxRows = aMaxRows;
514 0 : return NS_OK;
515 : }
516 :
517 : NS_IMETHODIMP
518 0 : nsFormFillController::GetShowImageColumn(bool *aShowImageColumn)
519 : {
520 0 : *aShowImageColumn = false;
521 0 : return NS_OK;
522 : }
523 :
524 0 : NS_IMETHODIMP nsFormFillController::SetShowImageColumn(bool aShowImageColumn)
525 : {
526 0 : return NS_ERROR_NOT_IMPLEMENTED;
527 : }
528 :
529 :
530 : NS_IMETHODIMP
531 0 : nsFormFillController::GetShowCommentColumn(bool *aShowCommentColumn)
532 : {
533 0 : *aShowCommentColumn = false;
534 0 : return NS_OK;
535 : }
536 :
537 0 : NS_IMETHODIMP nsFormFillController::SetShowCommentColumn(bool aShowCommentColumn)
538 : {
539 0 : return NS_ERROR_NOT_IMPLEMENTED;
540 : }
541 :
542 : NS_IMETHODIMP
543 0 : nsFormFillController::GetTimeout(uint32_t *aTimeout)
544 : {
545 0 : *aTimeout = mTimeout;
546 0 : return NS_OK;
547 : }
548 :
549 0 : NS_IMETHODIMP nsFormFillController::SetTimeout(uint32_t aTimeout)
550 : {
551 0 : mTimeout = aTimeout;
552 0 : return NS_OK;
553 : }
554 :
555 : NS_IMETHODIMP
556 0 : nsFormFillController::SetSearchParam(const nsAString &aSearchParam)
557 : {
558 0 : return NS_ERROR_NOT_IMPLEMENTED;
559 : }
560 :
561 : NS_IMETHODIMP
562 0 : nsFormFillController::GetSearchParam(nsAString &aSearchParam)
563 : {
564 0 : if (!mFocusedInput) {
565 0 : NS_WARNING("mFocusedInput is null for some reason! avoiding a crash. should find out why... - ben");
566 0 : return NS_ERROR_FAILURE; // XXX why? fix me.
567 : }
568 :
569 0 : mFocusedInput->GetName(aSearchParam);
570 0 : if (aSearchParam.IsEmpty()) {
571 0 : nsCOMPtr<Element> element = do_QueryInterface(mFocusedInput);
572 0 : element->GetId(aSearchParam);
573 : }
574 :
575 0 : return NS_OK;
576 : }
577 :
578 : NS_IMETHODIMP
579 0 : nsFormFillController::GetSearchCount(uint32_t *aSearchCount)
580 : {
581 0 : *aSearchCount = 1;
582 0 : return NS_OK;
583 : }
584 :
585 : NS_IMETHODIMP
586 0 : nsFormFillController::GetSearchAt(uint32_t index, nsACString & _retval)
587 : {
588 0 : if (mAutofillInputs.Get(mFocusedInputNode)) {
589 0 : nsCOMPtr<nsIAutoCompleteSearch> profileSearch = do_GetService("@mozilla.org/autocomplete/search;1?name=autofill-profiles");
590 0 : if (profileSearch) {
591 0 : _retval.AssignLiteral("autofill-profiles");
592 0 : return NS_OK;
593 : }
594 : }
595 :
596 0 : _retval.AssignLiteral("form-history");
597 0 : return NS_OK;
598 : }
599 :
600 : NS_IMETHODIMP
601 0 : nsFormFillController::GetTextValue(nsAString & aTextValue)
602 : {
603 0 : if (mFocusedInput) {
604 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
605 0 : HTMLInputElement::FromContent(content)->GetValue(aTextValue,
606 0 : CallerType::System);
607 : } else {
608 0 : aTextValue.Truncate();
609 : }
610 0 : return NS_OK;
611 : }
612 :
613 : NS_IMETHODIMP
614 0 : nsFormFillController::SetTextValue(const nsAString & aTextValue)
615 : {
616 0 : nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(mFocusedInput);
617 0 : if (editable) {
618 0 : mSuppressOnInput = true;
619 0 : editable->SetUserInput(aTextValue);
620 0 : mSuppressOnInput = false;
621 : }
622 0 : return NS_OK;
623 : }
624 :
625 : NS_IMETHODIMP
626 0 : nsFormFillController::SetTextValueWithReason(const nsAString & aTextValue,
627 : uint16_t aReason)
628 : {
629 0 : return SetTextValue(aTextValue);
630 : }
631 :
632 : NS_IMETHODIMP
633 0 : nsFormFillController::GetSelectionStart(int32_t *aSelectionStart)
634 : {
635 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
636 0 : if (!content) {
637 0 : return NS_ERROR_UNEXPECTED;
638 : }
639 0 : ErrorResult rv;
640 0 : *aSelectionStart =
641 0 : HTMLInputElement::FromContent(content)->GetSelectionStartIgnoringType(rv);
642 0 : return rv.StealNSResult();
643 : }
644 :
645 : NS_IMETHODIMP
646 0 : nsFormFillController::GetSelectionEnd(int32_t *aSelectionEnd)
647 : {
648 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
649 0 : if (!content) {
650 0 : return NS_ERROR_UNEXPECTED;
651 : }
652 0 : ErrorResult rv;
653 0 : *aSelectionEnd =
654 0 : HTMLInputElement::FromContent(content)->GetSelectionEndIgnoringType(rv);
655 0 : return rv.StealNSResult();
656 : }
657 :
658 : NS_IMETHODIMP
659 0 : nsFormFillController::SelectTextRange(int32_t aStartIndex, int32_t aEndIndex)
660 : {
661 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
662 0 : if (!content) {
663 0 : return NS_ERROR_UNEXPECTED;
664 : }
665 0 : ErrorResult rv;
666 : HTMLInputElement::FromContent(content)->
667 0 : SetSelectionRange(aStartIndex, aEndIndex, Optional<nsAString>(), rv);
668 0 : return rv.StealNSResult();
669 : }
670 :
671 : NS_IMETHODIMP
672 0 : nsFormFillController::OnSearchBegin()
673 : {
674 0 : return NS_OK;
675 : }
676 :
677 : NS_IMETHODIMP
678 0 : nsFormFillController::OnSearchComplete()
679 : {
680 0 : return NS_OK;
681 : }
682 :
683 : NS_IMETHODIMP
684 0 : nsFormFillController::OnTextEntered(nsIDOMEvent* aEvent,
685 : bool* aPrevent)
686 : {
687 0 : NS_ENSURE_ARG(aPrevent);
688 0 : NS_ENSURE_TRUE(mFocusedInput, NS_OK);
689 : // Fire off a DOMAutoComplete event
690 0 : nsCOMPtr<nsIDOMDocument> domDoc;
691 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mFocusedInput);
692 0 : element->GetOwnerDocument(getter_AddRefs(domDoc));
693 0 : NS_ENSURE_STATE(domDoc);
694 :
695 0 : nsCOMPtr<nsIDOMEvent> event;
696 0 : domDoc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
697 0 : NS_ENSURE_STATE(event);
698 :
699 0 : event->InitEvent(NS_LITERAL_STRING("DOMAutoComplete"), true, true);
700 :
701 : // XXXjst: We mark this event as a trusted event, it's up to the
702 : // callers of this to ensure that it's only called from trusted
703 : // code.
704 0 : event->SetTrusted(true);
705 :
706 0 : nsCOMPtr<EventTarget> targ = do_QueryInterface(mFocusedInput);
707 :
708 : bool defaultActionEnabled;
709 0 : targ->DispatchEvent(event, &defaultActionEnabled);
710 0 : *aPrevent = !defaultActionEnabled;
711 0 : return NS_OK;
712 : }
713 :
714 : NS_IMETHODIMP
715 0 : nsFormFillController::OnTextReverted(bool *_retval)
716 : {
717 0 : return NS_OK;
718 : }
719 :
720 : NS_IMETHODIMP
721 0 : nsFormFillController::GetConsumeRollupEvent(bool *aConsumeRollupEvent)
722 : {
723 0 : *aConsumeRollupEvent = false;
724 0 : return NS_OK;
725 : }
726 :
727 : NS_IMETHODIMP
728 0 : nsFormFillController::GetInPrivateContext(bool *aInPrivateContext)
729 : {
730 0 : if (!mFocusedInput) {
731 0 : *aInPrivateContext = false;
732 0 : return NS_OK;
733 : }
734 :
735 0 : nsCOMPtr<nsIDOMDocument> inputDoc;
736 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mFocusedInput);
737 0 : element->GetOwnerDocument(getter_AddRefs(inputDoc));
738 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(inputDoc);
739 0 : nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
740 0 : *aInPrivateContext = loadContext && loadContext->UsePrivateBrowsing();
741 0 : return NS_OK;
742 : }
743 :
744 : NS_IMETHODIMP
745 0 : nsFormFillController::GetNoRollupOnCaretMove(bool *aNoRollupOnCaretMove)
746 : {
747 0 : *aNoRollupOnCaretMove = false;
748 0 : return NS_OK;
749 : }
750 :
751 : NS_IMETHODIMP
752 0 : nsFormFillController::GetUserContextId(uint32_t* aUserContextId)
753 : {
754 0 : *aUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
755 0 : return NS_OK;
756 : }
757 :
758 : ////////////////////////////////////////////////////////////////////////
759 : //// nsIAutoCompleteSearch
760 :
761 : NS_IMETHODIMP
762 0 : nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAString &aSearchParam,
763 : nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
764 : {
765 : nsresult rv;
766 0 : nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mFocusedInputNode);
767 :
768 : // If the login manager has indicated it's responsible for this field, let it
769 : // handle the autocomplete. Otherwise, handle with form history.
770 : // This method is sometimes called in unit tests and from XUL without a focused node.
771 0 : if (mFocusedInputNode &&
772 0 : (mPwmgrInputs.Get(mFocusedInputNode) ||
773 0 : formControl->ControlType() == NS_FORM_INPUT_PASSWORD)) {
774 :
775 : // Handle the case where a password field is focused but
776 : // MarkAsLoginManagerField wasn't called because password manager is disabled.
777 0 : if (!mLoginManager) {
778 0 : mLoginManager = do_GetService("@mozilla.org/login-manager;1");
779 : }
780 :
781 0 : if (NS_WARN_IF(!mLoginManager)) {
782 0 : return NS_ERROR_FAILURE;
783 : }
784 :
785 : // XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
786 : // satchel manage the field?
787 0 : mLastListener = aListener;
788 0 : rv = mLoginManager->AutoCompleteSearchAsync(aSearchString,
789 : aPreviousResult,
790 : mFocusedInput,
791 0 : this);
792 0 : NS_ENSURE_SUCCESS(rv, rv);
793 : } else {
794 0 : mLastListener = aListener;
795 :
796 0 : nsCOMPtr<nsIAutoCompleteResult> datalistResult;
797 0 : if (mFocusedInput) {
798 0 : rv = PerformInputListAutoComplete(aSearchString,
799 0 : getter_AddRefs(datalistResult));
800 0 : NS_ENSURE_SUCCESS(rv, rv);
801 : }
802 :
803 0 : auto formAutoComplete = GetFormAutoComplete();
804 0 : NS_ENSURE_TRUE(formAutoComplete, NS_ERROR_FAILURE);
805 :
806 0 : formAutoComplete->AutoCompleteSearchAsync(aSearchParam,
807 : aSearchString,
808 : mFocusedInput,
809 : aPreviousResult,
810 : datalistResult,
811 0 : this);
812 0 : mLastFormAutoComplete = formAutoComplete;
813 : }
814 :
815 0 : return NS_OK;
816 : }
817 :
818 : nsresult
819 0 : nsFormFillController::PerformInputListAutoComplete(const nsAString& aSearch,
820 : nsIAutoCompleteResult** aResult)
821 : {
822 : // If an <input> is focused, check if it has a list="<datalist>" which can
823 : // provide the list of suggestions.
824 :
825 0 : MOZ_ASSERT(!mPwmgrInputs.Get(mFocusedInputNode));
826 : nsresult rv;
827 :
828 : nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
829 0 : do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
830 0 : NS_ENSURE_SUCCESS(rv, rv);
831 0 : rv = inputListAutoComplete->AutoCompleteSearch(aSearch,
832 : mFocusedInput,
833 0 : aResult);
834 0 : NS_ENSURE_SUCCESS(rv, rv);
835 :
836 0 : if (mFocusedInput) {
837 0 : nsCOMPtr<nsIDOMHTMLElement> list;
838 0 : mFocusedInput->GetList(getter_AddRefs(list));
839 :
840 : // Add a mutation observer to check for changes to the items in the <datalist>
841 : // and update the suggestions accordingly.
842 0 : nsCOMPtr<nsINode> node = do_QueryInterface(list);
843 0 : if (mListNode != node) {
844 0 : if (mListNode) {
845 0 : mListNode->RemoveMutationObserver(this);
846 0 : mListNode = nullptr;
847 : }
848 0 : if (node) {
849 0 : node->AddMutationObserverUnlessExists(this);
850 0 : mListNode = node;
851 : }
852 : }
853 : }
854 :
855 0 : return NS_OK;
856 : }
857 :
858 0 : void nsFormFillController::RevalidateDataList()
859 : {
860 0 : if (!mLastListener) {
861 0 : return;
862 : }
863 :
864 0 : nsCOMPtr<nsIAutoCompleteController> controller(do_QueryInterface(mLastListener));
865 0 : if (!controller) {
866 0 : return;
867 : }
868 :
869 0 : controller->StartSearch(mLastSearchString);
870 : }
871 :
872 : NS_IMETHODIMP
873 0 : nsFormFillController::StopSearch()
874 : {
875 : // Make sure to stop and clear this, otherwise the controller will prevent
876 : // mLastFormAutoComplete from being deleted.
877 0 : if (mLastFormAutoComplete) {
878 0 : mLastFormAutoComplete->StopAutoCompleteSearch();
879 0 : mLastFormAutoComplete = nullptr;
880 0 : } else if (mLoginManager) {
881 0 : mLoginManager->StopSearch();
882 : }
883 0 : return NS_OK;
884 : }
885 :
886 : ////////////////////////////////////////////////////////////////////////
887 : //// nsIFormAutoCompleteObserver
888 :
889 : NS_IMETHODIMP
890 0 : nsFormFillController::OnSearchCompletion(nsIAutoCompleteResult *aResult)
891 : {
892 0 : nsAutoString searchString;
893 0 : aResult->GetSearchString(searchString);
894 :
895 0 : mLastSearchString = searchString;
896 :
897 0 : if (mLastListener) {
898 0 : mLastListener->OnSearchResult(this, aResult);
899 : }
900 :
901 0 : return NS_OK;
902 : }
903 :
904 : ////////////////////////////////////////////////////////////////////////
905 : //// nsIDOMEventListener
906 :
907 : NS_IMETHODIMP
908 5 : nsFormFillController::HandleEvent(nsIDOMEvent* aEvent)
909 : {
910 5 : WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
911 5 : NS_ENSURE_STATE(internalEvent);
912 :
913 5 : switch (internalEvent->mMessage) {
914 : case eFocus:
915 4 : return Focus(aEvent);
916 : case eMouseDown:
917 0 : return MouseDown(aEvent);
918 : case eKeyPress:
919 0 : return KeyPress(aEvent);
920 : case eEditorInput:
921 : {
922 : nsCOMPtr<nsINode> input = do_QueryInterface(
923 0 : aEvent->InternalDOMEvent()->GetTarget());
924 0 : if (!IsTextControl(input)) {
925 0 : return NS_OK;
926 : }
927 :
928 0 : bool unused = false;
929 0 : return (!mSuppressOnInput && mController && mFocusedInput) ?
930 0 : mController->HandleText(&unused) : NS_OK;
931 : }
932 : case eBlur:
933 0 : if (mFocusedInput) {
934 0 : StopControllingInput();
935 : }
936 0 : return NS_OK;
937 : case eCompositionStart:
938 0 : NS_ASSERTION(mController, "should have a controller!");
939 0 : if (mController && mFocusedInput) {
940 0 : mController->HandleStartComposition();
941 : }
942 0 : return NS_OK;
943 : case eCompositionEnd:
944 0 : NS_ASSERTION(mController, "should have a controller!");
945 0 : if (mController && mFocusedInput) {
946 0 : mController->HandleEndComposition();
947 : }
948 0 : return NS_OK;
949 : case eContextMenu:
950 0 : if (mFocusedPopup) {
951 0 : mFocusedPopup->ClosePopup();
952 : }
953 0 : return NS_OK;
954 : case ePageHide:
955 : {
956 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(
957 2 : aEvent->InternalDOMEvent()->GetTarget());
958 1 : if (!doc) {
959 0 : return NS_OK;
960 : }
961 :
962 1 : if (mFocusedInput) {
963 0 : if (doc == mFocusedInputNode->OwnerDoc()) {
964 0 : StopControllingInput();
965 : }
966 : }
967 :
968 1 : RemoveForDocument(doc);
969 : }
970 1 : break;
971 : default:
972 : // Handling the default case to shut up stupid -Wswitch warnings.
973 : // One day compilers will be smarter...
974 0 : break;
975 : }
976 :
977 1 : return NS_OK;
978 : }
979 :
980 : void
981 1 : nsFormFillController::RemoveForDocument(nsIDocument* aDoc)
982 : {
983 1 : for (auto iter = mPwmgrInputs.Iter(); !iter.Done(); iter.Next()) {
984 0 : const nsINode* key = iter.Key();
985 0 : if (key && (!aDoc || key->OwnerDoc() == aDoc)) {
986 : // mFocusedInputNode's observer is tracked separately, so don't remove it
987 : // here.
988 0 : if (key != mFocusedInputNode) {
989 0 : const_cast<nsINode*>(key)->RemoveMutationObserver(this);
990 : }
991 0 : iter.Remove();
992 : }
993 : }
994 :
995 1 : for (auto iter = mAutofillInputs.Iter(); !iter.Done(); iter.Next()) {
996 0 : const nsINode* key = iter.Key();
997 0 : if (key && (!aDoc || key->OwnerDoc() == aDoc)) {
998 : // mFocusedInputNode's observer is tracked separately, so don't remove it
999 : // here.
1000 0 : if (key != mFocusedInputNode) {
1001 0 : const_cast<nsINode*>(key)->RemoveMutationObserver(this);
1002 : }
1003 0 : iter.Remove();
1004 : }
1005 : }
1006 1 : }
1007 :
1008 : bool
1009 0 : nsFormFillController::IsTextControl(nsINode* aNode)
1010 : {
1011 0 : nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aNode);
1012 0 : return formControl &&
1013 0 : formControl->IsSingleLineTextControl(false);
1014 : }
1015 :
1016 : void
1017 4 : nsFormFillController::MaybeStartControllingInput(nsIDOMHTMLInputElement* aInput)
1018 : {
1019 4 : nsCOMPtr<nsINode> inputNode = do_QueryInterface(aInput);
1020 4 : if (!inputNode) {
1021 4 : return;
1022 : }
1023 :
1024 0 : if (!IsTextControl(inputNode)) {
1025 0 : return;
1026 : }
1027 :
1028 0 : bool isReadOnly = false;
1029 0 : aInput->GetReadOnly(&isReadOnly);
1030 0 : if (isReadOnly) {
1031 0 : return;
1032 : }
1033 :
1034 0 : bool autocomplete = nsContentUtils::IsAutocompleteEnabled(aInput);
1035 :
1036 0 : nsCOMPtr<nsIDOMHTMLElement> datalist;
1037 0 : aInput->GetList(getter_AddRefs(datalist));
1038 0 : bool hasList = datalist != nullptr;
1039 :
1040 0 : bool isPwmgrInput = false;
1041 0 : nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aInput);
1042 0 : MOZ_ASSERT(formControl, "If we have a text control, we have a form control!");
1043 0 : if (mPwmgrInputs.Get(inputNode) ||
1044 0 : formControl->ControlType() == NS_FORM_INPUT_PASSWORD) {
1045 0 : isPwmgrInput = true;
1046 : }
1047 :
1048 0 : if (isPwmgrInput || hasList || autocomplete) {
1049 0 : StartControllingInput(aInput);
1050 : }
1051 : }
1052 :
1053 : nsresult
1054 4 : nsFormFillController::Focus(nsIDOMEvent* aEvent)
1055 : {
1056 : nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(
1057 8 : aEvent->InternalDOMEvent()->GetTarget());
1058 4 : MaybeStartControllingInput(input);
1059 :
1060 : // Bail if we didn't start controlling the input.
1061 4 : if (!mFocusedInputNode) {
1062 4 : return NS_OK;
1063 : }
1064 :
1065 : #ifndef ANDROID
1066 0 : nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mFocusedInputNode);
1067 0 : MOZ_ASSERT(formControl);
1068 :
1069 : // If this focus doesn't follow a right click within our specified
1070 : // threshold then show the autocomplete popup for all password fields.
1071 : // This is done to avoid showing both the context menu and the popup
1072 : // at the same time.
1073 : // We use a timestamp instead of a bool to avoid complexity when dealing with
1074 : // multiple input forms and the fact that a mousedown into an already focused
1075 : // field does not trigger another focus.
1076 :
1077 0 : if (formControl->ControlType() != NS_FORM_INPUT_PASSWORD) {
1078 0 : return NS_OK;
1079 : }
1080 :
1081 : // If we have not seen a right click yet, just show the popup.
1082 0 : if (mLastRightClickTimeStamp.IsNull()) {
1083 0 : ShowPopup();
1084 0 : return NS_OK;
1085 : }
1086 :
1087 0 : uint64_t timeDiff = (TimeStamp::Now() - mLastRightClickTimeStamp).ToMilliseconds();
1088 0 : if (timeDiff > mFocusAfterRightClickThreshold) {
1089 0 : ShowPopup();
1090 : }
1091 : #endif
1092 :
1093 0 : return NS_OK;
1094 : }
1095 :
1096 : nsresult
1097 0 : nsFormFillController::KeyPress(nsIDOMEvent* aEvent)
1098 : {
1099 0 : NS_ASSERTION(mController, "should have a controller!");
1100 0 : if (!mFocusedInput || !mController) {
1101 0 : return NS_OK;
1102 : }
1103 :
1104 0 : nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
1105 0 : if (!keyEvent) {
1106 0 : return NS_ERROR_FAILURE;
1107 : }
1108 :
1109 0 : bool cancel = false;
1110 0 : bool unused = false;
1111 :
1112 : uint32_t k;
1113 0 : keyEvent->GetKeyCode(&k);
1114 0 : switch (k) {
1115 : case nsIDOMKeyEvent::DOM_VK_DELETE:
1116 : #ifndef XP_MACOSX
1117 0 : mController->HandleDelete(&cancel);
1118 0 : break;
1119 : case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
1120 0 : mController->HandleText(&unused);
1121 0 : break;
1122 : #else
1123 : case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
1124 : {
1125 : bool isShift = false;
1126 : keyEvent->GetShiftKey(&isShift);
1127 :
1128 : if (isShift) {
1129 : mController->HandleDelete(&cancel);
1130 : } else {
1131 : mController->HandleText(&unused);
1132 : }
1133 :
1134 : break;
1135 : }
1136 : #endif
1137 : case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
1138 : case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
1139 : {
1140 : bool isCtrl, isAlt, isMeta;
1141 0 : keyEvent->GetCtrlKey(&isCtrl);
1142 0 : keyEvent->GetAltKey(&isAlt);
1143 0 : keyEvent->GetMetaKey(&isMeta);
1144 0 : if (isCtrl || isAlt || isMeta) {
1145 : break;
1146 : }
1147 : }
1148 : MOZ_FALLTHROUGH;
1149 : case nsIDOMKeyEvent::DOM_VK_UP:
1150 : case nsIDOMKeyEvent::DOM_VK_DOWN:
1151 : case nsIDOMKeyEvent::DOM_VK_LEFT:
1152 : case nsIDOMKeyEvent::DOM_VK_RIGHT:
1153 : {
1154 : // Get the writing-mode of the relevant input element,
1155 : // so that we can remap arrow keys if necessary.
1156 0 : mozilla::WritingMode wm;
1157 0 : if (mFocusedInputNode && mFocusedInputNode->IsElement()) {
1158 0 : mozilla::dom::Element *elem = mFocusedInputNode->AsElement();
1159 0 : nsIFrame *frame = elem->GetPrimaryFrame();
1160 0 : if (frame) {
1161 0 : wm = frame->GetWritingMode();
1162 : }
1163 : }
1164 0 : if (wm.IsVertical()) {
1165 0 : switch (k) {
1166 : case nsIDOMKeyEvent::DOM_VK_LEFT:
1167 0 : k = wm.IsVerticalLR() ? nsIDOMKeyEvent::DOM_VK_UP
1168 : : nsIDOMKeyEvent::DOM_VK_DOWN;
1169 0 : break;
1170 : case nsIDOMKeyEvent::DOM_VK_RIGHT:
1171 0 : k = wm.IsVerticalLR() ? nsIDOMKeyEvent::DOM_VK_DOWN
1172 : : nsIDOMKeyEvent::DOM_VK_UP;
1173 0 : break;
1174 : case nsIDOMKeyEvent::DOM_VK_UP:
1175 0 : k = nsIDOMKeyEvent::DOM_VK_LEFT;
1176 0 : break;
1177 : case nsIDOMKeyEvent::DOM_VK_DOWN:
1178 0 : k = nsIDOMKeyEvent::DOM_VK_RIGHT;
1179 0 : break;
1180 : }
1181 : }
1182 : }
1183 0 : mController->HandleKeyNavigation(k, &cancel);
1184 0 : break;
1185 : case nsIDOMKeyEvent::DOM_VK_ESCAPE:
1186 0 : mController->HandleEscape(&cancel);
1187 0 : break;
1188 : case nsIDOMKeyEvent::DOM_VK_TAB:
1189 0 : mController->HandleTab();
1190 0 : cancel = false;
1191 0 : break;
1192 : case nsIDOMKeyEvent::DOM_VK_RETURN:
1193 0 : mController->HandleEnter(false, aEvent, &cancel);
1194 0 : break;
1195 : }
1196 :
1197 0 : if (cancel) {
1198 0 : aEvent->PreventDefault();
1199 : // Don't let the page see the RETURN event when the popup is open
1200 : // (indicated by cancel=true) so sites don't manually submit forms
1201 : // (e.g. via submit.click()) without the autocompleted value being filled.
1202 : // Bug 286933 will fix this for other key events.
1203 0 : if (k == nsIDOMKeyEvent::DOM_VK_RETURN) {
1204 0 : aEvent->StopPropagation();
1205 : }
1206 : }
1207 :
1208 0 : return NS_OK;
1209 : }
1210 :
1211 : nsresult
1212 0 : nsFormFillController::MouseDown(nsIDOMEvent* aEvent)
1213 : {
1214 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aEvent));
1215 0 : if (!mouseEvent) {
1216 0 : return NS_ERROR_FAILURE;
1217 : }
1218 :
1219 : nsCOMPtr<nsIDOMHTMLInputElement> targetInput = do_QueryInterface(
1220 0 : aEvent->InternalDOMEvent()->GetTarget());
1221 0 : if (!targetInput) {
1222 0 : return NS_OK;
1223 : }
1224 :
1225 : int16_t button;
1226 0 : mouseEvent->GetButton(&button);
1227 :
1228 : // In case of a right click we set a timestamp that
1229 : // will be checked in Focus() to avoid showing
1230 : // both contextmenu and popup at the same time.
1231 0 : if (button == 2) {
1232 0 : mLastRightClickTimeStamp = TimeStamp::Now();
1233 0 : return NS_OK;
1234 : }
1235 :
1236 0 : if (button != 0) {
1237 0 : return NS_OK;
1238 : }
1239 :
1240 0 : return ShowPopup();
1241 : }
1242 :
1243 : NS_IMETHODIMP
1244 0 : nsFormFillController::ShowPopup()
1245 : {
1246 0 : bool isOpen = false;
1247 0 : GetPopupOpen(&isOpen);
1248 0 : if (isOpen) {
1249 0 : return SetPopupOpen(false);
1250 : }
1251 :
1252 0 : nsCOMPtr<nsIAutoCompleteInput> input;
1253 0 : mController->GetInput(getter_AddRefs(input));
1254 0 : if (!input) {
1255 0 : return NS_OK;
1256 : }
1257 :
1258 0 : nsAutoString value;
1259 0 : input->GetTextValue(value);
1260 0 : if (value.Length() > 0) {
1261 : // Show the popup with a filtered result set
1262 0 : mController->SetSearchString(EmptyString());
1263 0 : bool unused = false;
1264 0 : mController->HandleText(&unused);
1265 : } else {
1266 : // Show the popup with the complete result set. Can't use HandleText()
1267 : // because it doesn't display the popup if the input is blank.
1268 0 : bool cancel = false;
1269 0 : mController->HandleKeyNavigation(nsIDOMKeyEvent::DOM_VK_DOWN, &cancel);
1270 : }
1271 :
1272 0 : return NS_OK;
1273 : }
1274 :
1275 : ////////////////////////////////////////////////////////////////////////
1276 : //// nsFormFillController
1277 :
1278 : void
1279 1 : nsFormFillController::AddWindowListeners(nsPIDOMWindowOuter* aWindow)
1280 : {
1281 1 : if (!aWindow) {
1282 0 : return;
1283 : }
1284 :
1285 1 : EventTarget* target = aWindow->GetChromeEventHandler();
1286 :
1287 1 : if (!target) {
1288 0 : return;
1289 : }
1290 :
1291 3 : target->AddEventListener(NS_LITERAL_STRING("focus"), this,
1292 2 : true, false);
1293 3 : target->AddEventListener(NS_LITERAL_STRING("blur"), this,
1294 2 : true, false);
1295 3 : target->AddEventListener(NS_LITERAL_STRING("pagehide"), this,
1296 2 : true, false);
1297 3 : target->AddEventListener(NS_LITERAL_STRING("mousedown"), this,
1298 2 : true, false);
1299 3 : target->AddEventListener(NS_LITERAL_STRING("input"), this,
1300 2 : true, false);
1301 1 : target->AddEventListener(NS_LITERAL_STRING("keypress"), this, true, false);
1302 3 : target->AddEventListener(NS_LITERAL_STRING("compositionstart"), this,
1303 2 : true, false);
1304 3 : target->AddEventListener(NS_LITERAL_STRING("compositionend"), this,
1305 2 : true, false);
1306 3 : target->AddEventListener(NS_LITERAL_STRING("contextmenu"), this,
1307 2 : true, false);
1308 :
1309 : // Note that any additional listeners added should ensure that they ignore
1310 : // untrusted events, which might be sent by content that's up to no good.
1311 : }
1312 :
1313 : void
1314 0 : nsFormFillController::RemoveWindowListeners(nsPIDOMWindowOuter* aWindow)
1315 : {
1316 0 : if (!aWindow) {
1317 0 : return;
1318 : }
1319 :
1320 0 : StopControllingInput();
1321 :
1322 0 : nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
1323 0 : RemoveForDocument(doc);
1324 :
1325 0 : EventTarget* target = aWindow->GetChromeEventHandler();
1326 :
1327 0 : if (!target) {
1328 0 : return;
1329 : }
1330 :
1331 0 : target->RemoveEventListener(NS_LITERAL_STRING("focus"), this, true);
1332 0 : target->RemoveEventListener(NS_LITERAL_STRING("blur"), this, true);
1333 0 : target->RemoveEventListener(NS_LITERAL_STRING("pagehide"), this, true);
1334 0 : target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true);
1335 0 : target->RemoveEventListener(NS_LITERAL_STRING("input"), this, true);
1336 0 : target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
1337 0 : target->RemoveEventListener(NS_LITERAL_STRING("compositionstart"), this,
1338 0 : true);
1339 0 : target->RemoveEventListener(NS_LITERAL_STRING("compositionend"), this,
1340 0 : true);
1341 0 : target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this, true);
1342 : }
1343 :
1344 : void
1345 0 : nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput)
1346 : {
1347 : // Make sure we're not still attached to an input
1348 0 : StopControllingInput();
1349 :
1350 0 : if (!mController) {
1351 0 : return;
1352 : }
1353 :
1354 : // Find the currently focused docShell
1355 0 : nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(aInput);
1356 0 : int32_t index = GetIndexOfDocShell(docShell);
1357 0 : if (index < 0) {
1358 0 : return;
1359 : }
1360 :
1361 : // Cache the popup for the focused docShell
1362 0 : mFocusedPopup = mPopups.SafeElementAt(index);
1363 :
1364 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
1365 0 : if (!node) {
1366 0 : return;
1367 : }
1368 :
1369 0 : node->AddMutationObserverUnlessExists(this);
1370 0 : mFocusedInputNode = node;
1371 0 : mFocusedInput = aInput;
1372 :
1373 0 : nsCOMPtr<nsIDOMHTMLElement> list;
1374 0 : mFocusedInput->GetList(getter_AddRefs(list));
1375 0 : nsCOMPtr<nsINode> listNode = do_QueryInterface(list);
1376 0 : if (listNode) {
1377 0 : listNode->AddMutationObserverUnlessExists(this);
1378 0 : mListNode = listNode;
1379 : }
1380 :
1381 0 : mController->SetInput(this);
1382 : }
1383 :
1384 : void
1385 0 : nsFormFillController::StopControllingInput()
1386 : {
1387 0 : if (mListNode) {
1388 0 : mListNode->RemoveMutationObserver(this);
1389 0 : mListNode = nullptr;
1390 : }
1391 :
1392 0 : if (mController) {
1393 : // Reset the controller's input, but not if it has been switched
1394 : // to another input already, which might happen if the user switches
1395 : // focus by clicking another autocomplete textbox
1396 0 : nsCOMPtr<nsIAutoCompleteInput> input;
1397 0 : mController->GetInput(getter_AddRefs(input));
1398 0 : if (input == this) {
1399 0 : mController->SetInput(nullptr);
1400 : }
1401 : }
1402 :
1403 0 : if (mFocusedInputNode) {
1404 0 : MaybeRemoveMutationObserver(mFocusedInputNode);
1405 :
1406 0 : mFocusedInputNode = nullptr;
1407 0 : mFocusedInput = nullptr;
1408 : }
1409 :
1410 0 : if (mFocusedPopup) {
1411 0 : mFocusedPopup->ClosePopup();
1412 : }
1413 0 : mFocusedPopup = nullptr;
1414 0 : }
1415 :
1416 : nsIDocShell *
1417 0 : nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
1418 : {
1419 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
1420 0 : NS_ENSURE_TRUE(node, nullptr);
1421 :
1422 0 : nsCOMPtr<nsPIDOMWindowOuter> win = node->OwnerDoc()->GetWindow();
1423 0 : NS_ENSURE_TRUE(win, nullptr);
1424 :
1425 0 : return win->GetDocShell();
1426 : }
1427 :
1428 : nsPIDOMWindowOuter*
1429 1 : nsFormFillController::GetWindowForDocShell(nsIDocShell *aDocShell)
1430 : {
1431 2 : nsCOMPtr<nsIContentViewer> contentViewer;
1432 1 : aDocShell->GetContentViewer(getter_AddRefs(contentViewer));
1433 1 : NS_ENSURE_TRUE(contentViewer, nullptr);
1434 :
1435 2 : nsCOMPtr<nsIDOMDocument> domDoc;
1436 1 : contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
1437 2 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
1438 1 : NS_ENSURE_TRUE(doc, nullptr);
1439 :
1440 1 : return doc->GetWindow();
1441 : }
1442 :
1443 : int32_t
1444 0 : nsFormFillController::GetIndexOfDocShell(nsIDocShell *aDocShell)
1445 : {
1446 0 : if (!aDocShell) {
1447 0 : return -1;
1448 : }
1449 :
1450 : // Loop through our cached docShells looking for the given docShell
1451 0 : uint32_t count = mDocShells.Length();
1452 0 : for (uint32_t i = 0; i < count; ++i) {
1453 0 : if (mDocShells[i] == aDocShell) {
1454 0 : return i;
1455 : }
1456 : }
1457 :
1458 : // Recursively check the parent docShell of this one
1459 0 : nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(aDocShell);
1460 0 : nsCOMPtr<nsIDocShellTreeItem> parentItem;
1461 0 : treeItem->GetParent(getter_AddRefs(parentItem));
1462 0 : if (parentItem) {
1463 0 : nsCOMPtr<nsIDocShell> parentShell = do_QueryInterface(parentItem);
1464 0 : return GetIndexOfDocShell(parentShell);
1465 : }
1466 :
1467 0 : return -1;
1468 : }
1469 :
1470 6 : NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormFillController)
1471 :
1472 : NS_DEFINE_NAMED_CID(NS_FORMFILLCONTROLLER_CID);
1473 :
1474 : static const mozilla::Module::CIDEntry kSatchelCIDs[] = {
1475 : { &kNS_FORMFILLCONTROLLER_CID, false, nullptr, nsFormFillControllerConstructor },
1476 : { nullptr }
1477 : };
1478 :
1479 : static const mozilla::Module::ContractIDEntry kSatchelContracts[] = {
1480 : { "@mozilla.org/satchel/form-fill-controller;1", &kNS_FORMFILLCONTROLLER_CID },
1481 : { NS_FORMHISTORYAUTOCOMPLETE_CONTRACTID, &kNS_FORMFILLCONTROLLER_CID },
1482 : { nullptr }
1483 : };
1484 :
1485 : static const mozilla::Module kSatchelModule = {
1486 : mozilla::Module::kVersion,
1487 : kSatchelCIDs,
1488 : kSatchelContracts
1489 : };
1490 :
1491 : NSMODULE_DEFN(satchel) = &kSatchelModule;
|