diff --git a/browser/extensions/formautofill/test/browser/browser_cancel_attribute_change_task.js b/browser/extensions/formautofill/test/browser/browser_cancel_attribute_change_task.js index 6a32360f684d..af2a0c79476c 100644 --- a/browser/extensions/formautofill/test/browser/browser_cancel_attribute_change_task.js +++ b/browser/extensions/formautofill/test/browser/browser_cancel_attribute_change_task.js @@ -58,7 +58,7 @@ add_task( "@mozilla.org/satchel/form-fill-controller;1" ].getService(Ci.nsIFormFillController); Assert.equal( - formFillController.focusedInput?.id, + formFillController.focusedElement?.id, "address-level1", "formFillController has correct focusedInput" ); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 940ed6a51b20..6f014a2e757c 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -1308,15 +1308,20 @@ nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String, return rv; } -bool nsContentUtils::IsAutocompleteEnabled( - mozilla::dom::HTMLInputElement* aInput) { - MOZ_ASSERT(aInput, "aInput should not be null!"); +bool nsContentUtils::IsAutocompleteEnabled(mozilla::dom::Element* aElement) { + MOZ_ASSERT(aElement, "aElement should not be null!"); nsAutoString autocomplete; - aInput->GetAutocomplete(autocomplete); + + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + input->GetAutocomplete(autocomplete); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + textarea->GetAutocomplete(autocomplete); + } if (autocomplete.IsEmpty()) { - auto* form = aInput->GetForm(); + auto* control = nsGenericHTMLFormControlElement::FromNode(aElement); + auto* form = control->GetForm(); if (!form) { return true; } diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index b6489b8232f3..1e5a0cae6a2c 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2652,10 +2652,11 @@ class nsContentUtils { * NOTE: the caller has to make sure autocomplete makes sense for the * element's type. * - * @param aInput the input element to check. NOTE: aInput can't be null. + * @param aElement the input or textarea element to check. NOTE: aElement + * can't be null. * @return whether the input element has autocomplete enabled. */ - static bool IsAutocompleteEnabled(mozilla::dom::HTMLInputElement* aInput); + static bool IsAutocompleteEnabled(mozilla::dom::Element* aElement); enum AutocompleteAttrState : uint8_t { eAutocompleteAttrState_Unknown = 1, diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 07b330655ad9..da2d7e4a1fd1 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -1131,7 +1131,8 @@ JSObject* HTMLTextAreaElement::WrapNode(JSContext* aCx, return HTMLTextAreaElement_Binding::Wrap(aCx, this, aGivenProto); } -void HTMLTextAreaElement::GetAutocomplete(DOMString& aValue) { +void HTMLTextAreaElement::GetAutocomplete(nsAString& aValue) { + aValue.Truncate(); const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete); mAutocompleteAttrState = nsContentUtils::SerializeAutocompleteAttribute( diff --git a/dom/html/HTMLTextAreaElement.h b/dom/html/HTMLTextAreaElement.h index d2867e162994..0d8972731ba0 100644 --- a/dom/html/HTMLTextAreaElement.h +++ b/dom/html/HTMLTextAreaElement.h @@ -167,7 +167,7 @@ class HTMLTextAreaElement final : public TextControlElement, ValidityStateType aType) override; // Web IDL binding methods - void GetAutocomplete(DOMString& aValue); + void GetAutocomplete(nsAString& aValue); void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv) { SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv); } diff --git a/toolkit/components/formautofill/FormAutofillChild.sys.mjs b/toolkit/components/formautofill/FormAutofillChild.sys.mjs index 7600e1fe19e7..fcf64986c2bd 100644 --- a/toolkit/components/formautofill/FormAutofillChild.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillChild.sys.mjs @@ -138,7 +138,7 @@ export class FormAutofillChild extends JSWindowActorChild { this.#markAsAutofillField(fieldDetail); if ( - fieldDetail.element == lazy.FormAutofillContent.focusedInput && + fieldDetail.element == lazy.FormAutofillContent.focusedElement && !isUpdate ) { this.showPopupIfEmpty(fieldDetail.element, fieldDetail.fieldName); @@ -484,7 +484,7 @@ export class FormAutofillChild extends JSWindowActorChild { this._hasDOMContentLoadedHandler = true; doc.addEventListener( "DOMContentLoaded", - () => this.onFocusIn(lazy.FormAutofillContent.focusedInput), + () => this.onFocusIn(lazy.FormAutofillContent.focusedElement), { once: true } ); } diff --git a/toolkit/components/formautofill/FormAutofillContent.sys.mjs b/toolkit/components/formautofill/FormAutofillContent.sys.mjs index bbbb51757e9e..903992e718c8 100644 --- a/toolkit/components/formautofill/FormAutofillContent.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillContent.sys.mjs @@ -30,8 +30,8 @@ export var FormAutofillContent = { return Services.cpmm.sharedData.get("FormAutofill:savedFieldNames"); }, - get focusedInput() { - return formFillController.focusedInput; + get focusedElement() { + return formFillController.focusedElement; }, /** diff --git a/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs b/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs index 4ef8796bcf13..a603c7f40742 100644 --- a/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs +++ b/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs @@ -1401,19 +1401,19 @@ export class LoginManagerChild extends JSWindowActorChild { break; } case "PasswordManager:OnFieldAutoComplete": { - const { focusedInput } = lazy.gFormFillService; + const { focusedElement } = lazy.gFormFillService; const login = lazy.LoginHelper.vanillaObjectToLogin(msg.data); - this.onFieldAutoComplete(focusedInput, login); + this.onFieldAutoComplete(focusedElement, login); break; } case "PasswordManager:FillGeneratedPassword": { - const { focusedInput } = lazy.gFormFillService; - this.filledWithGeneratedPassword(focusedInput); + const { focusedElement } = lazy.gFormFillService; + this.filledWithGeneratedPassword(focusedElement); break; } case "PasswordManager:FillRelayUsername": { - const { focusedInput } = lazy.gFormFillService; - this.fillRelayUsername(focusedInput, msg.data); + const { focusedElement } = lazy.gFormFillService; + this.fillRelayUsername(focusedElement, msg.data); break; } } @@ -1428,7 +1428,7 @@ export class LoginManagerChild extends JSWindowActorChild { return; } - if (inputElement != lazy.gFormFillService.focusedInput) { + if (inputElement != lazy.gFormFillService.focusedElement) { lazy.log("Could not open popup on input that's no longer focused."); return; } @@ -3041,7 +3041,7 @@ export class LoginManagerChild extends JSWindowActorChild { Glean.pwmgr.formAutofillResult[autofillResult].add(1); if (usernameField) { - let focusedElement = lazy.gFormFillService.focusedInput; + let focusedElement = lazy.gFormFillService.focusedElement; if ( usernameField == focusedElement && ![ diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp index d2366c260a69..6b2d8dd0d3a9 100644 --- a/toolkit/components/satchel/nsFormFillController.cpp +++ b/toolkit/components/satchel/nsFormFillController.cpp @@ -9,11 +9,14 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/ErrorResult.h" #include "mozilla/EventListenerManager.h" +#include "mozilla/TextControlElement.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" // for Event #include "mozilla/dom/HTMLDataListElement.h" #include "mozilla/dom/HTMLInputElement.h" +#include "mozilla/dom/HTMLTextAreaElement.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/KeyboardEvent.h" #include "mozilla/dom/KeyboardEventBinding.h" #include "mozilla/dom/MouseEvent.h" @@ -64,7 +67,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFormFillController) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFormFillController) nsFormFillController::nsFormFillController() - : mFocusedInput(nullptr), + : mFocusedElement(nullptr), mRestartAfterAttributeChangeTask(nullptr), mListNode(nullptr), // The amount of time a context menu event supresses showing a @@ -96,9 +99,9 @@ nsFormFillController::~nsFormFillController() { mListNode->RemoveMutationObserver(this); mListNode = nullptr; } - if (mFocusedInput) { - MaybeRemoveMutationObserver(mFocusedInput); - mFocusedInput = nullptr; + if (mFocusedElement) { + MaybeRemoveMutationObserver(mFocusedElement); + mFocusedElement = nullptr; } RemoveForDocument(nullptr); } @@ -126,7 +129,7 @@ void nsFormFillController::AttributeChanged(mozilla::dom::Element* aElement, if ((aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::readonly || aAttribute == nsGkAtoms::autocomplete) && aNameSpaceID == kNameSpaceID_None) { - RefPtr focusedInput(mFocusedInput); + RefPtr focusedElement(mFocusedElement); // Reset the current state of the controller, unconditionally. StopControllingInput(); // Then restart based on the new values. We have to delay this @@ -136,10 +139,10 @@ void nsFormFillController::AttributeChanged(mozilla::dom::Element* aElement, // attribute change, cancel it. MaybeCancelAttributeChangeTask(); mRestartAfterAttributeChangeTask = - mozilla::NewCancelableRunnableMethod>( + mozilla::NewCancelableRunnableMethod>( "nsFormFillController::MaybeStartControllingInput", this, &nsFormFillController::MaybeStartControllingInputScheduled, - focusedInput); + focusedElement); RefPtr addrefedRunnable = mRestartAfterAttributeChangeTask; aElement->OwnerDoc()->Dispatch(addrefedRunnable.forget()); } @@ -151,9 +154,9 @@ void nsFormFillController::AttributeChanged(mozilla::dom::Element* aElement, MOZ_CAN_RUN_SCRIPT_BOUNDARY void nsFormFillController::MaybeStartControllingInputScheduled( - HTMLInputElement* aInput) { + Element* aElement) { mRestartAfterAttributeChangeTask = nullptr; - MaybeStartControllingInput(aInput); + MaybeStartControllingInput(aElement); } MOZ_CAN_RUN_SCRIPT_BOUNDARY @@ -215,8 +218,8 @@ void nsFormFillController::NodeWillBeDestroyed(nsINode* aNode) { if (aNode == mListNode) { mListNode = nullptr; RevalidateDataList(); - } else if (aNode == mFocusedInput) { - mFocusedInput = nullptr; + } else if (aNode == mFocusedElement) { + mFocusedElement = nullptr; } } @@ -232,28 +235,32 @@ void nsFormFillController::MaybeRemoveMutationObserver(nsINode* aNode) { //// nsIFormFillController NS_IMETHODIMP -nsFormFillController::MarkAsAutoCompletableField(HTMLInputElement* aInput) { +nsFormFillController::MarkAsAutoCompletableField(Element* aElement) { /* * Support other components implementing form autofill and handle autocomplete * for the field. */ - NS_ENSURE_STATE(aInput); + NS_ENSURE_STATE(aElement); + + if (!aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { + return NS_ERROR_UNEXPECTED; + } MOZ_LOG(sLogger, LogLevel::Verbose, - ("MarkAsAutoCompletableField: aInput = %p", aInput)); + ("MarkAsAutoCompletableField: aElement = %p", aElement)); - if (mAutoCompleteInputs.Get(aInput)) { + if (mAutoCompleteInputs.Get(aElement)) { return NS_OK; } - mAutoCompleteInputs.InsertOrUpdate(aInput, true); - aInput->AddMutationObserverUnlessExists(this); + mAutoCompleteInputs.InsertOrUpdate(aElement, true); + aElement->AddMutationObserverUnlessExists(this); - aInput->EnablePreview(); + EnablePreview(aElement); - if (nsFocusManager::GetFocusedElementStatic() == aInput) { - if (!mFocusedInput) { - MaybeStartControllingInput(aInput); + if (nsFocusManager::GetFocusedElementStatic() == aElement) { + if (!mFocusedElement) { + MaybeStartControllingInput(aElement); } else { // See `MarkAsLoginManagerField` for why this is needed. nsCOMPtr controller = mController; @@ -265,9 +272,9 @@ nsFormFillController::MarkAsAutoCompletableField(HTMLInputElement* aInput) { } NS_IMETHODIMP -nsFormFillController::GetFocusedInput(HTMLInputElement** aInput) { - *aInput = mFocusedInput; - NS_IF_ADDREF(*aInput); +nsFormFillController::GetFocusedElement(Element** aElement) { + *aElement = mFocusedElement; + NS_IF_ADDREF(*aElement); return NS_OK; } @@ -308,9 +315,9 @@ nsFormFillController::SetPopupOpen(bool aPopupOpen) { if (mFocusedPopup) { if (aPopupOpen) { // make sure input field is visible before showing popup (bug 320938) - nsCOMPtr content = mFocusedInput; + nsCOMPtr content = mFocusedElement; NS_ENSURE_STATE(content); - nsCOMPtr docShell = GetDocShellForInput(mFocusedInput); + nsCOMPtr docShell = GetDocShellForInput(mFocusedElement); NS_ENSURE_STATE(docShell); RefPtr presShell = docShell->GetPresShell(); NS_ENSURE_STATE(presShell); @@ -322,7 +329,7 @@ nsFormFillController::SetPopupOpen(bool aPopupOpen) { // mFocusedPopup can be destroyed after ScrollContentIntoView, see bug // 420089 if (mFocusedPopup) { - mFocusedPopup->OpenAutocompletePopup(this, mFocusedInput); + mFocusedPopup->OpenAutocompletePopup(this, mFocusedElement); } } else { mFocusedPopup->ClosePopup(); @@ -422,16 +429,17 @@ nsFormFillController::SetSearchParam(const nsAString& aSearchParam) { NS_IMETHODIMP nsFormFillController::GetSearchParam(nsAString& aSearchParam) { - if (!mFocusedInput) { + if (!mFocusedElement) { NS_WARNING( - "mFocusedInput is null for some reason! avoiding a crash. should find " + "mFocusedElement is null for some reason! avoiding a crash. should " + "find " "out why... - ben"); return NS_ERROR_FAILURE; // XXX why? fix me. } - mFocusedInput->GetName(aSearchParam); + GetName(mFocusedElement, aSearchParam); if (aSearchParam.IsEmpty()) { - mFocusedInput->GetId(aSearchParam); + mFocusedElement->GetId(aSearchParam); } return NS_OK; @@ -459,8 +467,8 @@ nsFormFillController::GetSearchAt(uint32_t index, nsACString& _retval) { NS_IMETHODIMP nsFormFillController::GetTextValue(nsAString& aTextValue) { - if (mFocusedInput) { - mFocusedInput->GetValue(aTextValue, CallerType::System); + if (mFocusedElement) { + GetValue(mFocusedElement, aTextValue); } else { aTextValue.Truncate(); } @@ -469,10 +477,10 @@ nsFormFillController::GetTextValue(nsAString& aTextValue) { NS_IMETHODIMP nsFormFillController::SetTextValue(const nsAString& aTextValue) { - if (mFocusedInput) { + if (mFocusedElement) { mSuppressOnInput = true; - mFocusedInput->SetUserInput(aTextValue, - *nsContentUtils::GetSystemPrincipal()); + SetUserInput(mFocusedElement, aTextValue, + *nsContentUtils::GetSystemPrincipal()); mSuppressOnInput = false; } @@ -481,33 +489,32 @@ nsFormFillController::SetTextValue(const nsAString& aTextValue) { NS_IMETHODIMP nsFormFillController::GetSelectionStart(int32_t* aSelectionStart) { - if (!mFocusedInput) { + if (!mFocusedElement) { return NS_ERROR_UNEXPECTED; } ErrorResult rv; - *aSelectionStart = mFocusedInput->GetSelectionStartIgnoringType(rv); + *aSelectionStart = GetSelectionStartInternal(mFocusedElement, rv); return rv.StealNSResult(); } NS_IMETHODIMP nsFormFillController::GetSelectionEnd(int32_t* aSelectionEnd) { - if (!mFocusedInput) { + if (!mFocusedElement) { return NS_ERROR_UNEXPECTED; } ErrorResult rv; - *aSelectionEnd = mFocusedInput->GetSelectionEndIgnoringType(rv); + *aSelectionEnd = GetSelectionEndInternal(mFocusedElement, rv); return rv.StealNSResult(); } MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsFormFillController::SelectTextRange(int32_t aStartIndex, int32_t aEndIndex) { - if (!mFocusedInput) { + if (!mFocusedElement) { return NS_ERROR_UNEXPECTED; } - RefPtr focusedInput(mFocusedInput); + RefPtr focusedInput(mFocusedElement); ErrorResult rv; - focusedInput->SetSelectionRange(aStartIndex, aEndIndex, Optional(), - rv); + SetSelectionRange(focusedInput, aStartIndex, aEndIndex, rv); return rv.StealNSResult(); } @@ -519,7 +526,7 @@ nsFormFillController::OnSearchComplete() { return NS_OK; } NS_IMETHODIMP nsFormFillController::OnTextEntered(Event* aEvent) { - NS_ENSURE_TRUE(mFocusedInput, NS_OK); + NS_ENSURE_TRUE(mFocusedElement, NS_OK); return NS_OK; } @@ -537,12 +544,12 @@ nsFormFillController::GetConsumeRollupEvent(bool* aConsumeRollupEvent) { NS_IMETHODIMP nsFormFillController::GetInPrivateContext(bool* aInPrivateContext) { - if (!mFocusedInput) { + if (!mFocusedElement) { *aInPrivateContext = false; return NS_OK; } - RefPtr doc = mFocusedInput->OwnerDoc(); + RefPtr doc = mFocusedElement->OwnerDoc(); nsCOMPtr loadContext = doc->GetLoadContext(); *aInPrivateContext = loadContext && loadContext->UsePrivateBrowsing(); return NS_OK; @@ -556,8 +563,8 @@ nsFormFillController::GetNoRollupOnCaretMove(bool* aNoRollupOnCaretMove) { NS_IMETHODIMP nsFormFillController::GetNoRollupOnEmptySearch(bool* aNoRollupOnEmptySearch) { - if (mFocusedInput && mFocusedPopup) { - return mFocusedPopup->GetNoRollupOnEmptySearch(mFocusedInput, + if (mFocusedElement && mFocusedPopup) { + return mFocusedPopup->GetNoRollupOnEmptySearch(mFocusedElement, aNoRollupOnEmptySearch); } @@ -586,36 +593,36 @@ nsFormFillController::StartSearch(const nsAString& aSearchString, const nsAString& aSearchParam, nsIAutoCompleteResult* aPreviousResult, nsIAutoCompleteObserver* aListener) { - MOZ_LOG(sLogger, LogLevel::Debug, ("StartSearch for %p", mFocusedInput)); + MOZ_LOG(sLogger, LogLevel::Debug, ("StartSearch for %p", mFocusedElement)); mLastListener = aListener; - if (mFocusedInput && mFocusedPopup) { - if (mAutoCompleteInputs.Get(mFocusedInput) || - mFocusedInput->HasBeenTypePassword()) { + if (mFocusedElement && mFocusedPopup) { + if (mAutoCompleteInputs.Get(mFocusedElement) || + HasBeenTypePassword(mFocusedElement)) { MOZ_LOG(sLogger, LogLevel::Debug, ("StartSearch: formautofill or login field")); - return mFocusedPopup->StartSearch(aSearchString, mFocusedInput, this); + return mFocusedPopup->StartSearch(aSearchString, mFocusedElement, this); } } MOZ_LOG(sLogger, LogLevel::Debug, ("StartSearch: form history field")); - bool addDataList = IsTextControl(mFocusedInput); + bool addDataList = IsTextControl(mFocusedElement); if (addDataList) { MaybeObserveDataListMutations(); } - return mFocusedPopup->StartSearch(aSearchString, mFocusedInput, this); + return mFocusedPopup->StartSearch(aSearchString, mFocusedElement, this); } void nsFormFillController::MaybeObserveDataListMutations() { // If an is focused, check if it has a list="" which can // provide the list of suggestions. - if (mFocusedInput) { - Element* list = mFocusedInput->GetList(); + if (mFocusedElement) { + Element* list = GetList(mFocusedElement); // Add a mutation observer to check for changes to the items in the // and update the suggestions accordingly. @@ -739,7 +746,7 @@ nsFormFillController::HandleEvent(Event* aEvent) { return NS_OK; } case eBlur: - if (mFocusedInput && !StaticPrefs::ui_popup_disable_autohide()) { + if (mFocusedElement && !StaticPrefs::ui_popup_disable_autohide()) { StopControllingInput(); } return NS_OK; @@ -768,7 +775,7 @@ nsFormFillController::HandleEvent(Event* aEvent) { return NS_OK; } - if (mFocusedInput && doc == mFocusedInput->OwnerDoc()) { + if (mFocusedElement && doc == mFocusedElement->OwnerDoc()) { StopControllingInput(); } @@ -815,9 +822,9 @@ void nsFormFillController::RemoveForDocument(Document* aDoc) { for (auto iter = mAutoCompleteInputs.Iter(); !iter.Done(); iter.Next()) { const nsINode* key = iter.Key(); if (key && (!aDoc || key->OwnerDoc() == aDoc)) { - // mFocusedInput's observer is tracked separately, so don't remove it + // mFocusedElement's observer is tracked separately, so don't remove it // here. - if (key != mFocusedInput) { + if (key != mFocusedElement) { const_cast(key)->RemoveMutationObserver(this); } iter.Remove(); @@ -827,38 +834,42 @@ void nsFormFillController::RemoveForDocument(Document* aDoc) { bool nsFormFillController::IsTextControl(nsINode* aNode) { const auto* formControl = nsIFormControl::FromNodeOrNull(aNode); - return formControl && formControl->IsSingleLineTextControl(false); + return formControl && formControl->IsTextControl(false); } -void nsFormFillController::MaybeStartControllingInput( - HTMLInputElement* aInput) { +void nsFormFillController::MaybeStartControllingInput(Element* aElement) { MOZ_LOG(sLogger, LogLevel::Verbose, - ("MaybeStartControllingInput for %p", aInput)); - if (!aInput) { + ("MaybeStartControllingInput for %p", aElement)); + if (!aElement) { return; } - bool hasList = !!aInput->GetList(); + bool hasList = !!GetList(aElement); - if (!IsTextControl(aInput)) { + if (!IsTextControl(aElement)) { // Even if this is not a text control yet, it can become one in the future if (hasList) { - StartControllingInput(aInput); + StartControllingInput(aElement); } return; } - if (mAutoCompleteInputs.Get(aInput) || aInput->HasBeenTypePassword() || - hasList || nsContentUtils::IsAutocompleteEnabled(aInput)) { - StartControllingInput(aInput); + if (mAutoCompleteInputs.Get(aElement) || HasBeenTypePassword(aElement) || + hasList || nsContentUtils::IsAutocompleteEnabled(aElement)) { + StartControllingInput(aElement); } } -nsresult nsFormFillController::HandleFocus(HTMLInputElement* aInput) { - MaybeStartControllingInput(aInput); +nsresult nsFormFillController::HandleFocus(Element* aElement) { + if (!aElement || + !aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { + return NS_OK; + } + + MaybeStartControllingInput(aElement); // Bail if we didn't start controlling the input. - if (!mFocusedInput) { + if (!mFocusedElement) { return NS_OK; } @@ -874,7 +885,7 @@ nsresult nsFormFillController::HandleFocus(HTMLInputElement* aInput) { // multiple input forms and the fact that a mousedown into an already focused // field does not trigger another focus. - if (!mFocusedInput->HasBeenTypePassword()) { + if (!HasBeenTypePassword(mFocusedElement)) { return NS_OK; } @@ -897,7 +908,7 @@ nsresult nsFormFillController::HandleFocus(HTMLInputElement* aInput) { nsresult nsFormFillController::Focus(Event* aEvent) { nsCOMPtr input = do_QueryInterface(aEvent->GetComposedTarget()); - return HandleFocus(MOZ_KnownLive(HTMLInputElement::FromNodeOrNull(input))); + return HandleFocus(MOZ_KnownLive(Element::FromNodeOrNull(input))); } nsresult nsFormFillController::KeyDown(Event* aEvent) { @@ -962,8 +973,8 @@ nsresult nsFormFillController::KeyDown(Event* aEvent) { // Get the writing-mode of the relevant input element, // so that we can remap arrow keys if necessary. mozilla::WritingMode wm; - if (mFocusedInput) { - nsIFrame* frame = mFocusedInput->GetPrimaryFrame(); + if (mFocusedElement) { + nsIFrame* frame = mFocusedElement->GetPrimaryFrame(); if (frame) { wm = frame->GetWritingMode(); } @@ -1024,7 +1035,10 @@ nsresult nsFormFillController::MouseDown(Event* aEvent) { } nsCOMPtr targetNode = do_QueryInterface(aEvent->GetComposedTarget()); - if (!HTMLInputElement::FromNodeOrNull(targetNode)) { + + auto* element = Element::FromNodeOrNull(targetNode); + if (!element || + !element->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { return NS_OK; } @@ -1085,39 +1099,40 @@ NS_IMETHODIMP nsFormFillController::GetPasswordPopupAutomaticallyOpened( return NS_OK; } -void nsFormFillController::StartControllingInput(HTMLInputElement* aInput) { - MOZ_LOG(sLogger, LogLevel::Verbose, ("StartControllingInput for %p", aInput)); +void nsFormFillController::StartControllingInput(Element* aElement) { + MOZ_LOG(sLogger, LogLevel::Verbose, + ("StartControllingInput for %p", aElement)); // Make sure we're not still attached to an input StopControllingInput(); - if (!mController || !aInput) { + if (!mController || !aElement) { return; } nsCOMPtr popup = - do_QueryActor("AutoComplete", aInput->OwnerDoc()); + do_QueryActor("AutoComplete", aElement->OwnerDoc()); if (!popup) { return; } mFocusedPopup = popup; - aInput->AddMutationObserverUnlessExists(this); - mFocusedInput = aInput; + aElement->AddMutationObserverUnlessExists(this); + mFocusedElement = aElement; - if (Element* list = mFocusedInput->GetList()) { + if (Element* list = GetList(mFocusedElement)) { list->AddMutationObserverUnlessExists(this); mListNode = list; } - if (!mFocusedInput->ReadOnly()) { + if (!ReadOnly(mFocusedElement)) { nsCOMPtr controller = mController; controller->SetInput(this); } } bool nsFormFillController::IsFocusedInputControlled() const { - return mFocusedInput && mController && !mFocusedInput->ReadOnly(); + return mFocusedElement && mController && !ReadOnly(mFocusedElement); } void nsFormFillController::StopControllingInput() { @@ -1142,10 +1157,10 @@ void nsFormFillController::StopControllingInput() { } MOZ_LOG(sLogger, LogLevel::Verbose, - ("StopControllingInput: Stopped controlling %p", mFocusedInput)); - if (mFocusedInput) { - MaybeRemoveMutationObserver(mFocusedInput); - mFocusedInput = nullptr; + ("StopControllingInput: Stopped controlling %p", mFocusedElement)); + if (mFocusedElement) { + MaybeRemoveMutationObserver(mFocusedElement); + mFocusedElement = nullptr; } if (mFocusedPopup) { @@ -1154,12 +1169,113 @@ void nsFormFillController::StopControllingInput() { mFocusedPopup = nullptr; } -nsIDocShell* nsFormFillController::GetDocShellForInput( - HTMLInputElement* aInput) { - NS_ENSURE_TRUE(aInput, nullptr); +nsIDocShell* nsFormFillController::GetDocShellForInput(Element* aElement) { + NS_ENSURE_TRUE(aElement, nullptr); - nsCOMPtr win = aInput->OwnerDoc()->GetWindow(); + nsCOMPtr win = aElement->OwnerDoc()->GetWindow(); NS_ENSURE_TRUE(win, nullptr); return win->GetDocShell(); } + +void nsFormFillController::GetName(mozilla::dom::Element* aElement, + nsAString& aValue) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + input->GetName(aValue); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + textarea->GetName(aValue); + } +} + +void nsFormFillController::GetValue(mozilla::dom::Element* aElement, + nsAString& aValue) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + input->GetValue(aValue, CallerType::System); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + textarea->GetValue(aValue); + } +} + +Element* nsFormFillController::GetList(mozilla::dom::Element* aElement) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + return input->GetList(); + } + return nullptr; +} + +bool nsFormFillController::HasBeenTypePassword( + mozilla::dom::Element* aElement) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + return input->HasBeenTypePassword(); + } + return false; +} + +bool nsFormFillController::ReadOnly(mozilla::dom::Element* aElement) const { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + return input->ReadOnly(); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + return textarea->ReadOnly(); + } + return false; +} + +uint32_t nsFormFillController::GetSelectionStartInternal( + mozilla::dom::Element* aElement, ErrorResult& aRv) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + return input->GetSelectionStartIgnoringType(aRv); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + Nullable start = textarea->GetSelectionStart(aRv); + if (!start.IsNull()) { + return start.Value(); + } + } + return 0; +} + +uint32_t nsFormFillController::GetSelectionEndInternal( + mozilla::dom::Element* aElement, ErrorResult& aRv) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + return input->GetSelectionEndIgnoringType(aRv); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + Nullable end = textarea->GetSelectionEnd(aRv); + if (!end.IsNull()) { + return end.Value(); + } + } + return 0; +} + +void nsFormFillController::SetSelectionRange(mozilla::dom::Element* aElement, + uint32_t aSelectionStart, + uint32_t aSelectionEnd, + ErrorResult& aRv) { + if (RefPtr input = + HTMLInputElement::FromNodeOrNull(aElement)) { + return input->SetSelectionRange(aSelectionStart, aSelectionEnd, + Optional(), aRv); + } else if (RefPtr textarea = + HTMLTextAreaElement::FromNodeOrNull(aElement)) { + return textarea->SetSelectionRange(aSelectionStart, aSelectionEnd, + Optional(), aRv); + } +} + +void nsFormFillController::SetUserInput(mozilla::dom::Element* aElement, + const nsAString& aValue, + nsIPrincipal& aSubjectPrincipal) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + input->SetUserInput(aValue, aSubjectPrincipal); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + textarea->SetUserInput(aValue, aSubjectPrincipal); + } +} + +void nsFormFillController::EnablePreview(mozilla::dom::Element* aElement) { + if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) { + input->EnablePreview(); + } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) { + textarea->EnablePreview(); + } + return; +} diff --git a/toolkit/components/satchel/nsFormFillController.h b/toolkit/components/satchel/nsFormFillController.h index e12600ed2201..846853e3e99a 100644 --- a/toolkit/components/satchel/nsFormFillController.h +++ b/toolkit/components/satchel/nsFormFillController.h @@ -27,9 +27,10 @@ class nsINode; namespace mozilla { class CancelableRunnable; +class ErrorResult; namespace dom { class EventTarget; -class HTMLInputElement; +class Element; } // namespace dom } // namespace mozilla @@ -65,13 +66,13 @@ class nsFormFillController final : public nsIFormFillController, MOZ_CAN_RUN_SCRIPT virtual ~nsFormFillController(); MOZ_CAN_RUN_SCRIPT - void StartControllingInput(mozilla::dom::HTMLInputElement* aInput); + void StartControllingInput(mozilla::dom::Element* aInput); MOZ_CAN_RUN_SCRIPT void StopControllingInput(); bool IsFocusedInputControlled() const; MOZ_CAN_RUN_SCRIPT - nsresult HandleFocus(mozilla::dom::HTMLInputElement* aInput); + nsresult HandleFocus(mozilla::dom::Element* aInput); void AttachListeners(mozilla::dom::EventTarget* aEventTarget); @@ -80,13 +81,12 @@ class nsFormFillController final : public nsIFormFillController, * StartControllingInput on it. */ MOZ_CAN_RUN_SCRIPT - void MaybeStartControllingInput(mozilla::dom::HTMLInputElement* aElement); + void MaybeStartControllingInput(mozilla::dom::Element* aElement); // clears the reference mRestartAfterAttributeChangeTask before running // MaybeStartControllingInput() MOZ_CAN_RUN_SCRIPT - void MaybeStartControllingInputScheduled( - mozilla::dom::HTMLInputElement* aElement); + void MaybeStartControllingInputScheduled(mozilla::dom::Element* aElement); // cancels a scheduled AttributeChangeTask and clears the reference // mRestartAfterAttributeChangeTask @@ -99,8 +99,7 @@ class nsFormFillController final : public nsIFormFillController, bool RowMatch(nsFormHistory* aHistory, uint32_t aIndex, const nsAString& aInputName, const nsAString& aInputValue); - inline nsIDocShell* GetDocShellForInput( - mozilla::dom::HTMLInputElement* aInput); + inline nsIDocShell* GetDocShellForInput(mozilla::dom::Element* aInput); void MaybeRemoveMutationObserver(nsINode* aNode); @@ -111,7 +110,7 @@ class nsFormFillController final : public nsIFormFillController, // members ////////////////////////////////////////// nsCOMPtr mController; - mozilla::dom::HTMLInputElement* mFocusedInput; + mozilla::dom::Element* mFocusedElement; RefPtr mRestartAfterAttributeChangeTask; // mListNode is a element which, is set, has the form fill @@ -141,6 +140,25 @@ class nsFormFillController final : public nsIFormFillController, bool mPasswordPopupAutomaticallyOpened; bool mAutoCompleteActive = false; bool mInvalidatePreviousResult = false; + + private: + void GetName(mozilla::dom::Element* aInput, nsAString& aValue); + void GetValue(mozilla::dom::Element* aInput, nsAString& aValue); + mozilla::dom::Element* GetList(mozilla::dom::Element* aInput); + bool HasBeenTypePassword(mozilla::dom::Element* aInput); + bool ReadOnly(mozilla::dom::Element* aInput) const; + uint32_t GetSelectionStartInternal(mozilla::dom::Element* aInput, + mozilla::ErrorResult& aRv); + uint32_t GetSelectionEndInternal(mozilla::dom::Element* aInput, + mozilla::ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT + void SetSelectionRange(mozilla::dom::Element* aInput, + uint32_t aSelectionStart, uint32_t aSelectionEnd, + mozilla::ErrorResult& aRv); + void SetUserInput(mozilla::dom::Element* aInput, const nsAString& aValue, + nsIPrincipal& aSubjectPrincipal); + void EnablePreview(mozilla::dom::Element* aInput); }; #endif // __nsFormFillController__ diff --git a/toolkit/components/satchel/nsIFormFillController.idl b/toolkit/components/satchel/nsIFormFillController.idl index 32d4e4ce892a..64bc150fe9f9 100644 --- a/toolkit/components/satchel/nsIFormFillController.idl +++ b/toolkit/components/satchel/nsIFormFillController.idl @@ -10,7 +10,6 @@ interface nsIAutoCompleteResult; webidl Document; webidl Element; webidl Event; -webidl HTMLInputElement; /* * nsIFormFillController is an interface for controlling form fill behavior @@ -25,9 +24,9 @@ webidl HTMLInputElement; interface nsIFormFillController : nsISupports { /* - * The input element the form fill controller is currently bound to. + * The input or textarea element the form fill controller is currently bound to. */ - readonly attribute HTMLInputElement focusedInput; + readonly attribute Element focusedElement; /* * Whether the autocomplete popup on a password field was automatically opened @@ -39,9 +38,9 @@ interface nsIFormFillController : nsISupports * Mark the specified element as being managed by autocomplete entry provider. * Autocomplete requests will be handed off to the AutoCompleteChild. * - * @param aInput - The HTML element to mark + * @param aElement - The HTML or