/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; |