toolkit/components/satchel/nsFormFillController.cpp

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

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

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

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

mercurial