Skip to content

Commit

Permalink
Bug 1518442 - Part 2: Implement Event-based form participation; r=sma…
Browse files Browse the repository at this point in the history
…ug,edgar

For Event-based form participation specification PR:
whatwg/html#4239

Differential Revision: https://phabricator.services.mozilla.com/D43986

--HG--
extra : moz-landing-system : lando
  • Loading branch information
johndai1984 committed Sep 6, 2019
1 parent c01608b commit 6635020
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 70 deletions.
62 changes: 46 additions & 16 deletions dom/base/FormData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@
using namespace mozilla;
using namespace mozilla::dom;

FormData::FormData(nsISupports* aOwner)
: HTMLFormSubmission(nullptr, EmptyString(), UTF_8_ENCODING, nullptr),
FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding,
Element* aOriginatingElement)
: HTMLFormSubmission(nullptr, EmptyString(), aEncoding,
aOriginatingElement),
mOwner(aOwner) {}

FormData::FormData(const FormData& aFormData)
: HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget,
aFormData.mEncoding, aFormData.mOriginatingElement) {
mOwner = aFormData.mOwner;
mFormData = aFormData.mFormData;
}

namespace {

already_AddRefed<File> GetOrCreateFileCalledBlob(Blob& aBlob,
Expand Down Expand Up @@ -290,8 +299,16 @@ already_AddRefed<FormData> FormData::Constructor(
const Optional<NonNull<HTMLFormElement> >& aFormElement, ErrorResult& aRv) {
RefPtr<FormData> formData = new FormData(aGlobal.GetAsSupports());
if (aFormElement.WasPassed()) {
aRv = aFormElement.Value().WalkFormElements(formData);
aRv = aFormElement.Value().ConstructEntryList(formData);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}

// Step 9. Return a shallow clone of entry list.
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set
formData = formData->Clone();
}

return formData.forget();
}

Expand All @@ -302,28 +319,41 @@ nsresult FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
nsACString& aContentTypeWithCharset,
nsACString& aCharset) const {
FSMultipartFormData fs(nullptr, EmptyString(), UTF_8_ENCODING, nullptr);
nsresult rv = CopySubmissionDataTo(&fs);
NS_ENSURE_SUCCESS(rv, rv);

for (uint32_t i = 0; i < mFormData.Length(); ++i) {
fs.GetContentType(aContentTypeWithCharset);
aCharset.Truncate();
*aContentLength = 0;
NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));

return NS_OK;
}

already_AddRefed<FormData> FormData::Clone() {
RefPtr<FormData> formData = new FormData(*this);
return formData.forget();
}

nsresult FormData::CopySubmissionDataTo(
HTMLFormSubmission* aFormSubmission) const {
MOZ_ASSERT(aFormSubmission, "Must have FormSubmission!");
for (size_t i = 0; i < mFormData.Length(); ++i) {
if (mFormData[i].wasNullBlob) {
MOZ_ASSERT(mFormData[i].value.IsUSVString());
fs.AddNameBlobOrNullPair(mFormData[i].name, nullptr);
aFormSubmission->AddNameBlobOrNullPair(mFormData[i].name, nullptr);
} else if (mFormData[i].value.IsUSVString()) {
fs.AddNameValuePair(mFormData[i].name,
mFormData[i].value.GetAsUSVString());
aFormSubmission->AddNameValuePair(mFormData[i].name,
mFormData[i].value.GetAsUSVString());
} else if (mFormData[i].value.IsBlob()) {
fs.AddNameBlobOrNullPair(mFormData[i].name,
mFormData[i].value.GetAsBlob());
aFormSubmission->AddNameBlobOrNullPair(mFormData[i].name,
mFormData[i].value.GetAsBlob());
} else {
MOZ_ASSERT(mFormData[i].value.IsDirectory());
fs.AddNameDirectoryPair(mFormData[i].name,
mFormData[i].value.GetAsDirectory());
aFormSubmission->AddNameDirectoryPair(
mFormData[i].name, mFormData[i].value.GetAsDirectory());
}
}

fs.GetContentType(aContentTypeWithCharset);
aCharset.Truncate();
*aContentLength = 0;
NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));

return NS_OK;
}
9 changes: 8 additions & 1 deletion dom/base/FormData.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class FormData final : public nsISupports,
public HTMLFormSubmission,
public nsWrapperCache {
private:
FormData(const FormData& aFormData);
~FormData() {}

struct FormDataTuple {
Expand All @@ -49,7 +50,11 @@ class FormData final : public nsISupports,
Directory* aDirectory);

public:
explicit FormData(nsISupports* aOwner = nullptr);
explicit FormData(nsISupports* aOwner = nullptr,
NotNull<const Encoding*> aEncoding = UTF_8_ENCODING,
Element* aOriginatingElement = nullptr);

already_AddRefed<FormData> Clone();

NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FormData)
Expand Down Expand Up @@ -136,6 +141,8 @@ class FormData final : public nsISupports,
nsACString& aContentTypeWithCharset,
nsACString& aCharset) const;

nsresult CopySubmissionDataTo(HTMLFormSubmission* aFormSubmission) const;

private:
nsCOMPtr<nsISupports> mOwner;

Expand Down
91 changes: 84 additions & 7 deletions dom/html/HTMLFormElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
// form submission
#include "HTMLFormSubmissionConstants.h"
#include "mozilla/dom/FormData.h"
#include "mozilla/dom/FormDataEvent.h"
#include "mozilla/Telemetry.h"
#include "nsIFormSubmitObserver.h"
#include "nsIObserverService.h"
Expand Down Expand Up @@ -117,7 +118,8 @@ HTMLFormElement::HTMLFormElement(
mDeferSubmission(false),
mNotifiedObservers(false),
mNotifiedObserversResult(false),
mEverTriedInvalidSubmit(false) {
mEverTriedInvalidSubmit(false),
mIsConstructingEntryList(false) {
// We start out valid.
AddStatesSilently(NS_EVENT_STATE_VALID);
}
Expand Down Expand Up @@ -504,7 +506,8 @@ nsresult HTMLFormElement::DoSubmitOrReset(WidgetEvent* aEvent,
if (eFormSubmit == aMessage) {
// Don't submit if we're not in a document or if we're in
// a sandboxed frame and form submit is disabled.
if (!doc || (doc->GetSandboxFlags() & SANDBOXED_FORMS)) {
if (mIsConstructingEntryList || !doc ||
(doc->GetSandboxFlags() & SANDBOXED_FORMS)) {
return NS_OK;
}
return DoSubmit(aEvent);
Expand Down Expand Up @@ -555,6 +558,13 @@ nsresult HTMLFormElement::DoSubmit(WidgetEvent* aEvent) {
// prepare the submission object
//
nsresult rv = BuildSubmission(getter_Transfers(submission), aEvent);

// Don't raise an error if form cannot navigate.
if (rv == NS_ERROR_NOT_AVAILABLE) {
mIsSubmitting = false;
return NS_OK;
}

if (NS_FAILED(rv)) {
mIsSubmitting = false;
return rv;
Expand Down Expand Up @@ -606,17 +616,33 @@ nsresult HTMLFormElement::BuildSubmission(HTMLFormSubmission** aFormSubmission,

nsresult rv;

//
// Walk over the form elements and call SubmitNamesValues() on them to get
// their data.
//
auto encoding = GetSubmitEncoding()->OutputEncoding();
RefPtr<FormData> formData =
new FormData(GetOwnerGlobal(), encoding, originatingElement);
rv = ConstructEntryList(formData);
NS_ENSURE_SUBMIT_SUCCESS(rv);

// Step 9. If form cannot navigate, then return.
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm
if (!GetComposedDoc()) {
return NS_ERROR_NOT_AVAILABLE;
}

//
// Get the submission object
//
rv = HTMLFormSubmission::GetFromForm(this, originatingElement,
rv = HTMLFormSubmission::GetFromForm(this, originatingElement, encoding,
aFormSubmission);
NS_ENSURE_SUBMIT_SUCCESS(rv);

//
// Dump the data into the submission object
//
rv = WalkFormElements(*aFormSubmission);
rv = formData->CopySubmissionDataTo(*aFormSubmission);
NS_ENSURE_SUBMIT_SUCCESS(rv);

return NS_OK;
Expand Down Expand Up @@ -857,8 +883,15 @@ nsresult HTMLFormElement::NotifySubmitObservers(nsIURI* aActionURL,
return rv;
}

nsresult HTMLFormElement::WalkFormElements(
HTMLFormSubmission* aFormSubmission) {
nsresult HTMLFormElement::ConstructEntryList(FormData* aFormData) {
MOZ_ASSERT(aFormData, "Must have FormData!");
if (mIsConstructingEntryList) {
// Step 2.2 of https://xhr.spec.whatwg.org/#dom-formdata.
return NS_ERROR_DOM_INVALID_STATE_ERR;
}

AutoRestore<bool> resetConstructingEntryList(mIsConstructingEntryList);
mIsConstructingEntryList = true;
// This shouldn't be called recursively, so use a rather large value
// for the preallocated buffer.
AutoTArray<RefPtr<nsGenericHTMLFormElement>, 100> sortedControls;
Expand All @@ -872,12 +905,56 @@ nsresult HTMLFormElement::WalkFormElements(
//
for (uint32_t i = 0; i < len; ++i) {
// Tell the control to submit its name/value pairs to the submission
sortedControls[i]->SubmitNamesValues(aFormSubmission);
sortedControls[i]->SubmitNamesValues(aFormData);
}

FormDataEventInit init;
init.mBubbles = true;
init.mCancelable = false;
init.mFormData = aFormData;
RefPtr<FormDataEvent> event =
FormDataEvent::Constructor(this, NS_LITERAL_STRING("formdata"), init);
event->SetTrusted(true);

EventDispatcher::DispatchDOMEvent(ToSupports(this), nullptr, event, nullptr,
nullptr);

return NS_OK;
}

NotNull<const Encoding*> HTMLFormElement::GetSubmitEncoding() {
nsAutoString acceptCharsetValue;
GetAttr(kNameSpaceID_None, nsGkAtoms::acceptcharset, acceptCharsetValue);

int32_t charsetLen = acceptCharsetValue.Length();
if (charsetLen > 0) {
int32_t offset = 0;
int32_t spPos = 0;
// get charset from charsets one by one
do {
spPos = acceptCharsetValue.FindChar(char16_t(' '), offset);
int32_t cnt = ((-1 == spPos) ? (charsetLen - offset) : (spPos - offset));
if (cnt > 0) {
nsAutoString uCharset;
acceptCharsetValue.Mid(uCharset, offset, cnt);

auto encoding = Encoding::ForLabelNoReplacement(uCharset);
if (encoding) {
return WrapNotNull(encoding);
}
}
offset = spPos + 1;
} while (spPos != -1);
}
// if there are no accept-charset or all the charset are not supported
// Get the charset from document
Document* doc = GetComposedDoc();
if (doc) {
return doc->GetDocumentCharacterSet();
}
return UTF_8_ENCODING;
}

// nsIForm

NS_IMETHODIMP_(uint32_t)
Expand Down
14 changes: 9 additions & 5 deletions dom/html/HTMLFormElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/HTMLFormSubmission.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIForm.h"
Expand All @@ -33,6 +32,7 @@ class EventChainPreVisitor;
namespace dom {
class HTMLFormControlsCollection;
class HTMLImageElement;
class FormData;

class HTMLFormElement final : public nsGenericHTMLElement,
public nsIWebProgressListener,
Expand Down Expand Up @@ -277,12 +277,13 @@ class HTMLFormElement final : public nsGenericHTMLElement,
bool SubmissionCanProceed(Element* aSubmitter);

/**
* Walk over the form elements and call SubmitNamesValues() on them to get
* their data pumped into the FormSubmitter.
* Contruct the entry list to get their data pumped into the FormData and
* fire a `formdata` event with the entry list in formData attribute.
* <https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set>
*
* @param aFormSubmission the form submission object
* @param aFormData the form data object
*/
nsresult WalkFormElements(HTMLFormSubmission* aFormSubmission);
nsresult ConstructEntryList(FormData* aFormData);

/**
* Whether the submission of this form has been ever prevented because of
Expand Down Expand Up @@ -611,8 +612,11 @@ class HTMLFormElement final : public nsGenericHTMLElement,
* being invalid.
*/
bool mEverTriedInvalidSubmit;
/** Whether we are constructing entry list */
bool mIsConstructingEntryList;

private:
NotNull<const Encoding*> GetSubmitEncoding();
~HTMLFormElement();
};

Expand Down
Loading

0 comments on commit 6635020

Please sign in to comment.