diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index e3fdbb4d32..cab5213da4 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -33,6 +33,7 @@ #include "nsFocusManager.h" #include "mozilla/dom/HTMLFormElement.h" #include "mozAutoDocUpdate.h" +#include "mozilla/Preferences.h" #define NS_IN_SUBMIT_CLICK (1 << 0) #define NS_OUTER_ACTIVATE_EVENT (1 << 1) @@ -186,6 +187,9 @@ HTMLButtonElement::ParseAttribute(int32_t aNamespaceID, } if (aAttribute == nsGkAtoms::formmethod) { + if (Preferences::GetBool("dom.dialog_element.enabled", false)) { + return aResult.ParseEnumValue(aValue, kFormMethodTableDialogEnabled, false); + } return aResult.ParseEnumValue(aValue, kFormMethodTable, false); } if (aAttribute == nsGkAtoms::formenctype) { diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index c8bb526379..34476cd741 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -321,6 +321,9 @@ HTMLFormElement::ParseAttribute(int32_t aNamespaceID, { if (aNamespaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::method) { + if (Preferences::GetBool("dom.dialog_element.enabled", false)) { + return aResult.ParseEnumValue(aValue, kFormMethodTableDialogEnabled, false); + } return aResult.ParseEnumValue(aValue, kFormMethodTable, false); } if (aAttribute == nsGkAtoms::enctype) { @@ -686,6 +689,26 @@ HTMLFormElement::DoSubmit(WidgetEvent* aEvent) // // perform the submission // + if (!submission) { + mIsSubmitting = false; +#ifdef DEBUG + HTMLDialogElement* dialog = nullptr; + for (nsIContent* parent = GetParent(); parent; + parent = parent->GetParent()) { + dialog = HTMLDialogElement::FromContentOrNull(parent); + if (dialog) { + break; + } + } + MOZ_ASSERT(!dialog || !dialog->Open()); +#endif + return NS_OK; + } + + if (DialogFormSubmission* dialogSubmission = + submission->GetAsDialogSubmission()) { + return SubmitDialog(dialogSubmission); + } return SubmitSubmission(submission); } @@ -722,8 +745,10 @@ HTMLFormElement::BuildSubmission(HTMLFormSubmission** aFormSubmission, // // Dump the data into the submission object // - rv = WalkFormElements(*aFormSubmission); - NS_ENSURE_SUBMIT_SUCCESS(rv); + if (!(*aFormSubmission)->GetAsDialogSubmission()) { + rv = WalkFormElements(*aFormSubmission); + NS_ENSURE_SUBMIT_SUCCESS(rv); + } return NS_OK; } @@ -862,6 +887,22 @@ HTMLFormElement::SubmitSubmission(HTMLFormSubmission* aFormSubmission) return rv; } +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-dialog +nsresult HTMLFormElement::SubmitDialog(DialogFormSubmission* aFormSubmission) { + mIsSubmitting = false; + + // Close the dialog subject. If there is a result, let that be the return + // value. + HTMLDialogElement* dialog = aFormSubmission->DialogElement(); + MOZ_ASSERT(dialog); + + Optional retValue; + retValue = &aFormSubmission->ReturnValue(); + dialog->Close(retValue); + + return NS_OK; +} + nsresult HTMLFormElement::DoSecureToInsecureSubmitCheck(nsIURI* aActionURL, bool* aCancelSubmit) diff --git a/dom/html/HTMLFormElement.h b/dom/html/HTMLFormElement.h index f13eab9acc..102e24fdf9 100644 --- a/dom/html/HTMLFormElement.h +++ b/dom/html/HTMLFormElement.h @@ -483,6 +483,12 @@ protected: */ nsresult SubmitSubmission(HTMLFormSubmission* aFormSubmission); + /** + * Submit a form[method=dialog] + * @param aFormSubmission the submission object + */ + nsresult SubmitDialog(DialogFormSubmission* aFormSubmission); + /** * Notify any submit observers of the submit. * diff --git a/dom/html/HTMLFormSubmission.cpp b/dom/html/HTMLFormSubmission.cpp index d447f51476..d098783dd7 100644 --- a/dom/html/HTMLFormSubmission.cpp +++ b/dom/html/HTMLFormSubmission.cpp @@ -944,6 +944,30 @@ HTMLFormSubmission::GetFromForm(nsGenericHTMLElement* aForm, charset.AssignLiteral("UTF-8"); } + if (method == NS_FORM_METHOD_DIALOG) { + HTMLDialogElement* dialog = nullptr; + for (nsIContent* parent = aForm->GetParent(); parent; + parent = parent->GetParent()) { + dialog = HTMLDialogElement::FromContentOrNull(parent); + if (dialog) { + break; + } + } + + // If there isn't one, or if it does not have an open attribute, do + // nothing. + if (!dialog || !dialog->Open()) { + return NS_OK; + } + + nsAutoString result; + if (aOriginatingElement) { + aOriginatingElement->ResultForDialogSubmit(result); + } + *aFormSubmission = new DialogFormSubmission(result, charset, aOriginatingElement, dialog); + return NS_OK; + } + // Choose encoder if (method == NS_FORM_METHOD_POST && enctype == NS_FORM_ENCTYPE_MULTIPART) { diff --git a/dom/html/HTMLFormSubmission.h b/dom/html/HTMLFormSubmission.h index 506aa668df..d2ea8df8b5 100644 --- a/dom/html/HTMLFormSubmission.h +++ b/dom/html/HTMLFormSubmission.h @@ -7,6 +7,7 @@ #define mozilla_dom_HTMLFormSubmission_h #include "mozilla/Attributes.h" +#include "mozilla/dom/HTMLDialogElement.h" #include "nsCOMPtr.h" #include "nsIContent.h" #include "nsNCRFallbackEncoderWrapper.h" @@ -121,6 +122,8 @@ public: return mOriginatingElement.get(); } + virtual DialogFormSubmission* GetAsDialogSubmission() { return nullptr; } + protected: /** * Can only be constructed by subclasses. @@ -168,6 +171,50 @@ private: nsNCRFallbackEncoderWrapper mEncoder; }; + +class DialogFormSubmission final : public HTMLFormSubmission { + public: + DialogFormSubmission(nsAString& aResult, + const nsACString& aCharset, Element* aSubmitter, + HTMLDialogElement* aDialogElement) + : HTMLFormSubmission(aCharset, aSubmitter) + , mDialogElement(aDialogElement) + , mReturnValue(aResult) { + } + + nsresult AddNameValuePair(const nsAString& aName, + const nsAString& aValue) override { + MOZ_CRASH("This method should not be called"); + return NS_OK; + } + + nsresult AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override { + MOZ_CRASH("This method should not be called"); + return NS_OK; + } + + nsresult AddNameDirectoryPair(const nsAString& aName, + Directory* aDirectory) override { + MOZ_CRASH("This method should not be called"); + return NS_OK; + } + + nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override { + MOZ_CRASH("This method should not be called"); + return NS_OK; + } + + DialogFormSubmission* GetAsDialogSubmission() override { return this; } + + HTMLDialogElement* DialogElement() { return mDialogElement; } + + nsString& ReturnValue() { return mReturnValue; } + + private: + const RefPtr mDialogElement; + nsString mReturnValue; +}; + /** * Handle multipart/form-data encoding, which does files as well as normal * inputs. This always does POST. diff --git a/dom/html/HTMLFormSubmissionConstants.h b/dom/html/HTMLFormSubmissionConstants.h index 261af61652..d3a880c58a 100644 --- a/dom/html/HTMLFormSubmissionConstants.h +++ b/dom/html/HTMLFormSubmissionConstants.h @@ -14,6 +14,13 @@ static const nsAttrValue::EnumTable kFormMethodTable[] = { { nullptr, 0 } }; +static const nsAttrValue::EnumTable kFormMethodTableDialogEnabled[] = { + {"get", NS_FORM_METHOD_GET}, + {"post", NS_FORM_METHOD_POST}, + {"dialog", NS_FORM_METHOD_DIALOG}, + {nullptr, 0} +}; + // Default method is 'get'. static const nsAttrValue::EnumTable* kFormDefaultMethod = &kFormMethodTable[0]; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index dd2d134f8e..718e65c435 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -1482,6 +1482,26 @@ NS_IMPL_STRING_ATTR(HTMLInputElement, Placeholder, placeholder) NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLInputElement, Type, type, kInputDefaultType->tag) +void HTMLInputElement::ResultForDialogSubmit(nsAString& aResult) { + if (mType == NS_FORM_INPUT_IMAGE) { + // Get a property set by the frame to find out where it was clicked. + nsIntPoint* lastClickedPoint = + static_cast(GetProperty(nsGkAtoms::imageClickedPoint)); + int32_t x, y; + if (lastClickedPoint) { + x = lastClickedPoint->x; + y = lastClickedPoint->y; + } else { + x = y = 0; + } + aResult.AppendInt(x); + aResult.AppendLiteral(","); + aResult.AppendInt(y); + } else { + GetAttr(kNameSpaceID_None, nsGkAtoms::value, aResult); + } +} + NS_IMETHODIMP HTMLInputElement::GetAutocomplete(nsAString& aValue) { @@ -5916,6 +5936,9 @@ HTMLInputElement::ParseAttribute(int32_t aNamespaceID, return aResult.ParseEnumValue(aValue, kFormMethodTable, false); } if (aAttribute == nsGkAtoms::formenctype) { + if (Preferences::GetBool("dom.dialog_element.enabled", false)) { + return aResult.ParseEnumValue(aValue, kFormMethodTableDialogEnabled, false); + } return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false); } if (aAttribute == nsGkAtoms::autocomplete) { diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 24fc7454ac..44e1f9c1ca 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -968,6 +968,8 @@ protected: const nsAttrValue* aOldValue, bool aNotify) override; + virtual void ResultForDialogSubmit(nsAString& aResult) override; + /** * Dispatch a select event. Returns true if the event was not cancelled. */ diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 56812b445b..d2b7f45e9c 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -861,6 +861,10 @@ public: static bool IsScrollGrabAllowed(JSContext*, JSObject*); + virtual inline void ResultForDialogSubmit(nsAString& aResult) { + GetAttr(kNameSpaceID_None, nsGkAtoms::value, aResult); + } + protected: /** * Add/remove this element to the documents name cache diff --git a/dom/html/nsIForm.h b/dom/html/nsIForm.h index 44eedc5103..6c2b1d8623 100644 --- a/dom/html/nsIForm.h +++ b/dom/html/nsIForm.h @@ -12,6 +12,7 @@ class nsIFormControl; #define NS_FORM_METHOD_GET 0 #define NS_FORM_METHOD_POST 1 +#define NS_FORM_METHOD_DIALOG 2 #define NS_FORM_ENCTYPE_URLENCODED 0 #define NS_FORM_ENCTYPE_MULTIPART 1 #define NS_FORM_ENCTYPE_TEXTPLAIN 2 diff --git a/dom/html/nsIFormControl.h b/dom/html/nsIFormControl.h index 067ac7cae8..1815c88543 100644 --- a/dom/html/nsIFormControl.h +++ b/dom/html/nsIFormControl.h @@ -16,6 +16,7 @@ namespace dom { class Element; class HTMLFieldSetElement; class HTMLFormSubmission; +class DialogFormSubmission; } // namespace dom } // namespace mozilla