Coverage Report

Created: 2018-09-25 14:53

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