Bug 1928604 - P3. Make FormFillController to support textarea r=NeilDeakin,credential-management-reviewers,edgar
Depends on D240335 Differential Revision: https://phabricator.services.mozilla.com/D240337
This commit is contained in:
@@ -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"
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ export var FormAutofillContent = {
|
||||
return Services.cpmm.sharedData.get("FormAutofill:savedFieldNames");
|
||||
},
|
||||
|
||||
get focusedInput() {
|
||||
return formFillController.focusedInput;
|
||||
get focusedElement() {
|
||||
return formFillController.focusedElement;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 &&
|
||||
![
|
||||
|
||||
@@ -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<HTMLInputElement> focusedInput(mFocusedInput);
|
||||
RefPtr<Element> 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<RefPtr<HTMLInputElement>>(
|
||||
mozilla::NewCancelableRunnableMethod<RefPtr<Element>>(
|
||||
"nsFormFillController::MaybeStartControllingInput", this,
|
||||
&nsFormFillController::MaybeStartControllingInputScheduled,
|
||||
focusedInput);
|
||||
focusedElement);
|
||||
RefPtr<Runnable> 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<nsIAutoCompleteController> 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<nsIContent> content = mFocusedInput;
|
||||
nsCOMPtr<nsIContent> content = mFocusedElement;
|
||||
NS_ENSURE_STATE(content);
|
||||
nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(mFocusedInput);
|
||||
nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(mFocusedElement);
|
||||
NS_ENSURE_STATE(docShell);
|
||||
RefPtr<PresShell> 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<HTMLInputElement> focusedInput(mFocusedInput);
|
||||
RefPtr<Element> focusedInput(mFocusedElement);
|
||||
ErrorResult rv;
|
||||
focusedInput->SetSelectionRange(aStartIndex, aEndIndex, Optional<nsAString>(),
|
||||
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<Document> doc = mFocusedInput->OwnerDoc();
|
||||
RefPtr<Document> doc = mFocusedElement->OwnerDoc();
|
||||
nsCOMPtr<nsILoadContext> 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 <input> is focused, check if it has a list="<datalist>" 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
|
||||
// <datalist> 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<nsINode*>(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<nsIContent> 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<nsINode> 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<nsIAutoCompletePopup> 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<nsIAutoCompleteController> 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<nsPIDOMWindowOuter> win = aInput->OwnerDoc()->GetWindow();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> 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<uint32_t> 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<uint32_t> 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<HTMLInputElement> input =
|
||||
HTMLInputElement::FromNodeOrNull(aElement)) {
|
||||
return input->SetSelectionRange(aSelectionStart, aSelectionEnd,
|
||||
Optional<nsAString>(), aRv);
|
||||
} else if (RefPtr<HTMLTextAreaElement> textarea =
|
||||
HTMLTextAreaElement::FromNodeOrNull(aElement)) {
|
||||
return textarea->SetSelectionRange(aSelectionStart, aSelectionEnd,
|
||||
Optional<nsAString>(), 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;
|
||||
}
|
||||
|
||||
@@ -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<nsIAutoCompleteController> mController;
|
||||
mozilla::dom::HTMLInputElement* mFocusedInput;
|
||||
mozilla::dom::Element* mFocusedElement;
|
||||
RefPtr<mozilla::CancelableRunnable> mRestartAfterAttributeChangeTask;
|
||||
|
||||
// mListNode is a <datalist> 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__
|
||||
|
||||
@@ -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 <input> element as being managed by autocomplete entry provider.
|
||||
* Autocomplete requests will be handed off to the AutoCompleteChild.
|
||||
*
|
||||
* @param aInput - The HTML <input> element to mark
|
||||
* @param aElement - The HTML <input> or <textarea> element to mark
|
||||
*/
|
||||
[can_run_script] void markAsAutoCompletableField(in HTMLInputElement aInput);
|
||||
[can_run_script] void markAsAutoCompletableField(in Element aElement);
|
||||
|
||||
/*
|
||||
* Open the autocomplete popup, if possible.
|
||||
|
||||
Reference in New Issue
Block a user