徐锦洪 发表于 2024-10-16 06:59:53

Chromium 前端form表单提交过程分析c++

一、本文以一个简单的 HTML 表单,包罗两个文本输入框和一个提交按钮:

<form action="demo_form.php">
First name: <input type="text" name="fname"><br>
Last name: <input type="text" name="lname"><br>
<input type="submit" value="提交">
</form> 测试时候可以打开菜鸟教程在线编辑器 (runoob.com)运行以上代码
二、看下c++form表单接口定义:

2.1)、form接口定义文件:

third_party\blink\renderer\core\html\forms\html_form_element.idl
method="get|post|dialog"

// https://html.spec.whatwg.org/C/#the-form-element

[
    Exposed=Window,
    HTMLConstructor,
    LegacyOverrideBuiltIns
] interface HTMLFormElement : HTMLElement {
    attribute DOMString acceptCharset;
    attribute USVString action;
    attribute DOMString autocomplete;
    attribute DOMString enctype;
    attribute DOMString encoding;
    attribute DOMString method;
    attribute DOMString name;
    attribute boolean noValidate;
    attribute DOMString target;
    attribute DOMString rel;
    readonly attribute DOMTokenList relList;

    readonly attribute HTMLFormControlsCollection elements;
    readonly attribute long length;
    getter Element (unsigned long index);
    // FIXME: This getter should not have .
    getter (RadioNodeList or Element) (DOMString name);

    void submit();
    void requestSubmit(optional HTMLElement? submitter = null);
    void reset();
    boolean checkValidity();
    boolean reportValidity();
};
2.2)、blink下form接口实现:

third_party\blink\renderer\core\html\forms\html_form_element.h
third_party\blink\renderer\core\html\forms\html_form_element.cc
用于查找和遍历form元素

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_FORM_ELEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_FORM_ELEMENT_H_

#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
#include "third_party/blink/renderer/core/html/forms/radio_button_group_scope.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/loader/form_submission.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"

namespace blink {

class DOMTokenList;
class Event;
class HTMLFormControlElement;
class HTMLFormControlsCollection;
class HTMLImageElement;
class ListedElement;
class RelList;
class V8UnionElementOrRadioNodeList;

class CORE_EXPORT HTMLFormElement final : public HTMLElement {
DEFINE_WRAPPERTYPEINFO();

public:
enum RelAttribute {
    kNone = 0,
    kNoReferrer = 1 << 0,
    kNoOpener = 1 << 1,
    kOpener = 1 << 2,
};

explicit HTMLFormElement(Document&);
~HTMLFormElement() override;
void Trace(Visitor*) const override;

HTMLFormControlsCollection* elements();
void GetNamedElements(const AtomicString&, HeapVector<Member<Element>>&);

unsigned length() const;
HTMLElement* item(unsigned index);

String action() const;
void setAction(const AtomicString&);

String enctype() const { return attributes_.EncodingType(); }
void setEnctype(const AtomicString&);

String encoding() const { return attributes_.EncodingType(); }
void setEncoding(const AtomicString& value) { setEnctype(value); }

DOMTokenList& relList() const;

bool HasRel(RelAttribute relation) const;

bool ShouldAutocomplete() const;

void Associate(ListedElement&);
void Disassociate(ListedElement&);
void Associate(HTMLImageElement&);
void Disassociate(HTMLImageElement&);
void DidAssociateByParser();

void PrepareForSubmission(const Event*,
                            HTMLFormControlElement* submit_button);
void submitFromJavaScript();
void requestSubmit(ExceptionState& exception_state);
void requestSubmit(HTMLElement* submitter, ExceptionState& exception_state);
void reset();

void AttachLayoutTree(AttachContext& context) override;
void DetachLayoutTree(bool performing_reattach) override;

void SubmitImplicitly(const Event&, bool from_implicit_submission_trigger);

String GetName() const;

bool NoValidate() const;

const AtomicString& Action() const;

String method() const;
void setMethod(const AtomicString&);
FormSubmission::SubmitMethod Method() const { return attributes_.Method(); }

// Find the 'default button.'
// https://html.spec.whatwg.org/C/#default-button
HTMLFormControlElement* FindDefaultButton() const;

bool checkValidity();
bool reportValidity();
bool MatchesValidityPseudoClasses() const final;
bool IsValidElement() final;

RadioButtonGroupScope& GetRadioButtonGroupScope() {
    return radio_button_group_scope_;
}

const ListedElement::List& ListedElements(
      bool include_shadow_trees = false) const;
const HeapVector<Member<HTMLImageElement>>& ImageElements();

V8UnionElementOrRadioNodeList* AnonymousNamedGetter(const AtomicString& name);
void InvalidateDefaultButtonStyle() const;

// 'construct the entry list'
// https://html.spec.whatwg.org/C/#constructing-the-form-data-set
// Returns nullptr if this form is already running this function.
FormData* ConstructEntryList(HTMLFormControlElement* submit_button,
                               const WTF::TextEncoding& encoding);

uint64_t UniqueRendererFormId() const { return unique_renderer_form_id_; }

void InvalidateListedElementsIncludingShadowTrees();

private:
InsertionNotificationRequest InsertedInto(ContainerNode&) override;
void RemovedFrom(ContainerNode&) override;
void FinishParsingChildren() override;

void HandleLocalEvents(Event&) override;

void ParseAttribute(const AttributeModificationParams&) override;
bool IsURLAttribute(const Attribute&) const override;
bool HasLegalLinkAttribute(const QualifiedName&) const override;

NamedItemType GetNamedItemType() const override {
    return NamedItemType::kName;
}

void SubmitDialog(FormSubmission*);
void ScheduleFormSubmission(const Event*,
                              HTMLFormControlElement* submit_button);

void CollectListedElements(
      const Node& root,
      ListedElement::List& elements,
      ListedElement::List* elements_including_shadow_trees = nullptr,
      bool in_shadow_tree = false) const;
void CollectImageElements(Node& root, HeapVector<Member<HTMLImageElement>>&);

// Returns true if the submission should proceed.
bool ValidateInteractively();

// Validates each of the controls, and stores controls of which 'invalid'
// event was not canceled to the specified vector. Returns true if there
// are any invalid controls in this form.
bool CheckInvalidControlsAndCollectUnhandled(ListedElement::List*);

Element* ElementFromPastNamesMap(const AtomicString&);
void AddToPastNamesMap(Element*, const AtomicString& past_name);
void RemoveFromPastNamesMap(HTMLElement&);

typedef HeapHashMap<AtomicString, Member<Element>> PastNamesMap;

FormSubmission::Attributes attributes_;
Member<PastNamesMap> past_names_map_;

RadioButtonGroupScope radio_button_group_scope_;

// Do not access listed_elements_ directly. Use ListedElements() instead.
ListedElement::List listed_elements_;
// Do not access listed_elements_including_shadow_trees_ directly. Use
// ListedElements(true) instead.
ListedElement::List listed_elements_including_shadow_trees_;
// Do not access image_elements_ directly. Use ImageElements() instead.
HeapVector<Member<HTMLImageElement>> image_elements_;

uint64_t unique_renderer_form_id_;

base::OnceClosure cancel_last_submission_;

bool is_submitting_ = false;
bool in_user_js_submit_event_ = false;
bool is_constructing_entry_list_ = false;

bool listed_elements_are_dirty_ : 1;
bool listed_elements_including_shadow_trees_are_dirty_ : 1;
bool image_elements_are_dirty_ : 1;
bool has_elements_associated_by_parser_ : 1;
bool has_elements_associated_by_form_attribute_ : 1;
bool did_finish_parsing_children_ : 1;
bool is_in_reset_function_ : 1;

Member<RelList> rel_list_;
unsigned rel_attribute_ = 0;
};

}// namespace blink

#endif// THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_FORM_ELEMENT_H_
2.3)、v8下form接口实现:

        out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_html_form_element.h
       out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_html_form_element.cc
截取部分定义:
void MethodAttributeGetCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {

RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_HTMLFormElement_method_Getter");
BLINK_BINDINGS_TRACE_EVENT("HTMLFormElement.method.get");



v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Object> v8_receiver = info.This();
HTMLFormElement* blink_receiver = V8HTMLFormElement::ToWrappableUnsafe(isolate, v8_receiver);
auto&& return_value = blink_receiver->method();
bindings::V8SetReturnValue(info, return_value, isolate, bindings::V8ReturnValue::kNonNullable);
}

void MethodAttributeSetCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {

RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_HTMLFormElement_method_Setter");
BLINK_BINDINGS_TRACE_EVENT("HTMLFormElement.method.set");


v8::Isolate* isolate = info.GetIsolate();
const ExceptionContextType exception_context_type = ExceptionContextType::kAttributeSet;
const char* const class_like_name = "HTMLFormElement";
const char* const property_name = "method";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);

//
CEReactionsScope ce_reactions_scope;

v8::Local<v8::Object> v8_receiver = info.This();
HTMLFormElement* blink_receiver = V8HTMLFormElement::ToWrappableUnsafe(isolate, v8_receiver);
v8::Local<v8::Value> v8_property_value = info;
auto&& arg1_value = NativeValueTraits<IDLString>::NativeValue(isolate, v8_property_value, exception_state);
if (UNLIKELY(exception_state.HadException())) {
return;
}
blink_receiver->setMethod(arg1_value);

} 2.4)、form_datat数据填充接口:

     FormData用来构建FormSubmission对象重要存储form表单内的元素参数:
      比方: 
 First name: <input type="text" name="fname"><br>
 Last name: <input type="text" name="lname"><br> 
会转换成FirstName=Mickey&LastName=Mouse,末了放到url请求参数内里。
结果:
https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse
hird_party\blink\renderer\core\html\forms\form_data.idl
// https://xhr.spec.whatwg.org/#interface-formdata

typedef (File or USVString) FormDataEntryValue;

[
    Exposed=(Window,Worker)
] interface FormData {
    constructor(optional HTMLFormElement form, optional HTMLElement? submitter = null);
    void append(USVString name, USVString value);
    void append(USVString name, Blob value, optional USVString filename);
    void delete(USVString name);
    FormDataEntryValue? get(USVString name);
    sequence<FormDataEntryValue> getAll(USVString name);
    boolean has(USVString name);
    void set(USVString name, USVString value);
    void set(USVString name, Blob value, optional USVString filename);
    iterable<USVString, FormDataEntryValue>;
};
blink和v8定义:
third_party\blink\renderer\core\html\forms\form_data.h
third_party\blink\renderer\core\html\forms\form_data.cc
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_

#include "third_party/blink/renderer/bindings/core/v8/iterable.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_form_data.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"

namespace blink {

class Blob;
class File;
class FormControlState;
class HTMLFormElement;
class ScriptState;
class ExecutionContext;

class CORE_EXPORT FormData final : public ScriptWrappable,
                                 public PairSyncIterable<FormData> {
DEFINE_WRAPPERTYPEINFO();

public:
static FormData* Create(ExceptionState& exception_state) {
    return MakeGarbageCollected<FormData>();
}
static FormData* Create(HTMLFormElement* form,
                        ExceptionState& exception_state);
static FormData* Create(HTMLFormElement* form,
                        HTMLElement* submitter,
                        ExceptionState& exception_state);

explicit FormData(const WTF::TextEncoding&);
// Clones form_data.This clones |form_data.entries_| Vector, but
// doesn't clone entries in it because they are immutable.
FormData(const FormData& form_data);
FormData();
void Trace(Visitor*) const override;

// FormData IDL interface.
void append(const String& name, const String& value);
void append(ScriptState*,
            const String& name,
            Blob*,
            const String& filename = String());
void deleteEntry(const String& name);
V8FormDataEntryValue* get(const String& name);
HeapVector<Member<V8FormDataEntryValue>> getAll(const String& name);
bool has(const String& name);
void set(const String& name, const String& value);
void set(const String& name, Blob*, const String& filename = String());

// Internal functions.

const WTF::TextEncoding& Encoding() const { return encoding_; }
std::string Encode(const String& key) const;
class Entry;
const HeapVector<Member<const Entry>>& Entries() const { return entries_; }
size_t size() const { return entries_.size(); }
void append(const String& name, Blob*, const String& filename = String());
void AppendFromElement(const String& name, int value);
void AppendFromElement(const String& name, File* file);
void AppendFromElement(const String& name, const String& value);

// This flag is true if this FormData is created with a <form>, and its
// associated elements contain a non-empty password field.
bool ContainsPasswordData() const { return contains_password_data_; }
void SetContainsPasswordData(bool flag) { contains_password_data_ = flag; }

scoped_refptr<EncodedFormData> EncodeFormData(
      EncodedFormData::EncodingType = EncodedFormData::kFormURLEncoded);
scoped_refptr<EncodedFormData> EncodeMultiPartFormData();

void AppendToControlState(FormControlState& state) const;
static FormData* CreateFromControlState(ExecutionContext& execution_context,
                                          const FormControlState& state,
                                          wtf_size_t& index);

private:
void SetEntry(const Entry*);
IterationSource* CreateIterationSource(ScriptState*,
                                       ExceptionState&) override;

WTF::TextEncoding encoding_;
// Entry pointers in entries_ never be nullptr.
HeapVector<Member<const Entry>> entries_;
bool contains_password_data_ = false;
};

// Represents entry, which is a pair of a name and a value.
// https://xhr.spec.whatwg.org/#concept-formdata-entry
// Entry objects are immutable.
class FormData::Entry final : public GarbageCollected<FormData::Entry> {
public:
Entry(const String& name, const String& value);
Entry(const String& name, Blob* blob, const String& filename);
void Trace(Visitor*) const;

bool IsString() const { return !blob_; }
bool isFile() const { return blob_ != nullptr; }
const String& name() const { return name_; }
const String& Value() const { return value_; }
Blob* GetBlob() const { return blob_.Get(); }
CORE_EXPORT File* GetFile() const;
const String& Filename() const { return filename_; }

private:
const String name_;
const String value_;
const Member<Blob> blob_;
const String filename_;
};

}// namespace blink

#endif// THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_
out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_form_data.h
out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_form_data.cc
截图部分实现:
void KeysOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_FormData_keys");
BLINK_BINDINGS_TRACE_EVENT("FormData.keys");

v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Object> v8_receiver = info.This();
FormData* blink_receiver = V8FormData::ToWrappableUnsafe(isolate, v8_receiver);
v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();
ScriptState* receiver_script_state = ScriptState::From(receiver_context);
ScriptState* script_state = receiver_script_state;
const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "FormData";
const char* const property_name = "keys";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
auto&& return_value = blink_receiver->keysForBinding(script_state, exception_state);
if (UNLIKELY(exception_state.HadException())) {
return;
}
bindings::V8SetReturnValue(info, return_value, blink_receiver);
}

void ValuesOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_FormData_values");
BLINK_BINDINGS_TRACE_EVENT("FormData.values");


v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Object> v8_receiver = info.This();
FormData* blink_receiver = V8FormData::ToWrappableUnsafe(isolate, v8_receiver);
v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();
ScriptState* receiver_script_state = ScriptState::From(receiver_context);
ScriptState* script_state = receiver_script_state;
const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "FormData";
const char* const property_name = "values";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
auto&& return_value = blink_receiver->valuesForBinding(script_state, exception_state);
if (UNLIKELY(exception_state.HadException())) {
return;
}
bindings::V8SetReturnValue(info, return_value, blink_receiver);
}
2.5)、form submit 管理类FormSubmission

   网页点击submit会调用到FormSubmission。
third_party\blink\renderer\core\loader\form_submission.h
third_party\blink\renderer\core\loader\form_submission.cc

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_

#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/frame/policy_container.mojom-blink.h"
#include "third_party/blink/public/mojom/frame/triggering_event_info.mojom-blink-forward.h"
#include "third_party/blink/public/web/web_frame_load_type.h"
#include "third_party/blink/renderer/core/loader/frame_loader_types.h"
#include "third_party/blink/renderer/core/loader/navigation_policy.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"

namespace blink {

class Element;
class EncodedFormData;
class Event;
class Frame;
class HTMLFormControlElement;
class HTMLFormElement;
class LocalDOMWindow;
class ResourceRequest;
class SourceLocation;

class FormSubmission final : public GarbageCollected<FormSubmission> {
public:
enum SubmitMethod { kGetMethod, kPostMethod, kDialogMethod };

class Attributes {
    DISALLOW_NEW();

   public:
    Attributes()
      : method_(kGetMethod),
          is_multi_part_form_(false),
          encoding_type_("application/x-www-form-urlencoded") {}
    Attributes(const Attributes&) = delete;
    Attributes& operator=(const Attributes&) = delete;

    SubmitMethod Method() const { return method_; }
    static SubmitMethod ParseMethodType(const String&);
    void UpdateMethodType(const String&);
    static String MethodString(SubmitMethod);

    const String& Action() const { return action_; }
    void ParseAction(const String&);

    const AtomicString& Target() const { return target_; }
    void SetTarget(const AtomicString& target) { target_ = target; }

    const AtomicString& EncodingType() const { return encoding_type_; }
    static AtomicString ParseEncodingType(const String&);
    void UpdateEncodingType(const String&);
    bool IsMultiPartForm() const { return is_multi_part_form_; }

    const String& AcceptCharset() const { return accept_charset_; }
    void SetAcceptCharset(const String& value) { accept_charset_ = value; }

    void CopyFrom(const Attributes&);

   private:
    SubmitMethod method_;
    bool is_multi_part_form_;

    String action_;
    AtomicString target_;
    AtomicString encoding_type_;
    String accept_charset_;
};

// Create FormSubmission
//
// This returns nullptr if form submission is not allowed for the given
// arguments. For example, if navigation policy for the event is
// `kNavigationPolicyLinkPreview`.
static FormSubmission* Create(HTMLFormElement*,
                              const Attributes&,
                              const Event*,
                              HTMLFormControlElement* submit_button);

FormSubmission(
      SubmitMethod,
      const KURL& action,
      const AtomicString& target,
      const AtomicString& content_type,
      Element* submitter,
      scoped_refptr<EncodedFormData>,
      const Event*,
      NavigationPolicy navigation_policy,
      mojom::blink::TriggeringEventInfo triggering_event_info,
      ClientNavigationReason reason,
      std::unique_ptr<ResourceRequest> resource_request,
      Frame* target_frame,
      WebFrameLoadType load_type,
      LocalDOMWindow* origin_window,
      const LocalFrameToken& initiator_frame_token,
      std::unique_ptr<SourceLocation> source_location,
      mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>
          initiator_policy_container_keep_alive_handle);
// FormSubmission for DialogMethod
explicit FormSubmission(const String& result);

void Trace(Visitor*) const;

void Navigate();

KURL RequestURL() const;

SubmitMethod Method() const { return method_; }
const KURL& Action() const { return action_; }
EncodedFormData* Data() const { return form_data_.get(); }

const String& Result() const { return result_; }

Frame* TargetFrame() const { return target_frame_.Get(); }

private:
// FIXME: Hold an instance of Attributes instead of individual members.
SubmitMethod method_;
KURL action_;
AtomicString target_;
AtomicString content_type_;
Member<Element> submitter_;
scoped_refptr<EncodedFormData> form_data_;
NavigationPolicy navigation_policy_;
mojom::blink::TriggeringEventInfo triggering_event_info_;
String result_;
ClientNavigationReason reason_;
std::unique_ptr<ResourceRequest> resource_request_;
Member<Frame> target_frame_;
WebFrameLoadType load_type_;
Member<LocalDOMWindow> origin_window_;
LocalFrameToken initiator_frame_token_;

// Since form submissions are scheduled asynchronously, we need to store the
// source location when we create the form submission and then pass it over to
// the `FrameLoadRequest`. Capturing the source location later when creating
// the `FrameLoadRequest` will not return the correct location.
std::unique_ptr<SourceLocation> source_location_;

// Since form submissions are scheduled asynchronously, we need to keep a
// handle to the initiator PolicyContainerHost. This ensures that it remains
// available in the browser until we create the NavigationRequest.
mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle>
      initiator_policy_container_keep_alive_handle_;
};

}// namespace blink

#endif// THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_
2.6)、submit_input类对应前端<input type="submit":

third_party\blink\renderer\core\html\forms\submit_input_type.h
third_party\blink\renderer\core\html\forms\submit_input_type.cc

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_INPUT_TYPE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_INPUT_TYPE_H_

#include "third_party/blink/renderer/core/html/forms/base_button_input_type.h"

namespace blink {

class SubmitInputType final : public BaseButtonInputType {
public:
explicit SubmitInputType(HTMLInputElement& element);

private:
void AppendToFormData(FormData&) const override;
bool SupportsRequired() const override;
void HandleDOMActivateEvent(Event&) override;
bool CanBeSuccessfulSubmitButton() override;
String DefaultLabel() const override;
bool IsTextButton() const override;
void ValueAttributeChanged() override;
};

template <>
struct DowncastTraits<SubmitInputType> {
static bool AllowFrom(const InputType& type) {
    return type.IsSubmitInputType();
}
};

}// namespace blink

#endif// THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_INPUT_TYPE_H_
三、看下form表单提交过程:

form表单提交过程:
1、render历程点击提交<input type="submit" value="提交">
2、render历程在HTMLFormElement类中构建 FormSubmission并剖析<input>内容、从而构建出请求参数:FirstName=Mickey&LastName=Mouse 以及
获取method="get"
/*
First name: <input type="text" name="FirstName" value="Mickey"><br>
Last name: <input type="text" name="LastName" value="Mouse"><br>
*/
末了拼接出请求URL
https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse
3、给主历程发送message.set_method_name("CreateNewWindow");
 在新标签中打开https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse。

============================优雅的分割线===================================

1、render历程点击提交<input type="submit" value="提交">

   SubmitInputType::HandleDOMActivateEvent(Event& event)
void SubmitInputType::HandleDOMActivateEvent(Event& event) {
if (GetElement().IsDisabledFormControl() || !GetElement().Form())
    return;
// Event handlers can run.
GetElement().Form()->PrepareForSubmission(&event, &GetElement());
event.SetDefaultHandled();
} https://i-blog.csdnimg.cn/direct/261032f77b514dae908ddb35e2960362.png
2、HTMLFormElement::ScheduleFormSubmission 函数

调用 FormSubmission* form_submission =
      FormSubmission::Create(this, attributes_, event, submit_button);
void HTMLFormElement::ScheduleFormSubmission(
    const Event* event,
    HTMLFormControlElement* submit_button) {
LocalFrameView* view = GetDocument().View();
LocalFrame* frame = GetDocument().GetFrame();
if (!view || !frame || !frame->GetPage())
    return;

// https://html.spec.whatwg.org/C/#form-submission-algorithm
// 2. If form document is not connected, has no associated browsing context,
// or its active sandboxing flag set has its sandboxed forms browsing
// context flag set, then abort these steps without doing anything.
if (!isConnected()) {
    GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
      mojom::ConsoleMessageSource::kJavaScript,
      mojom::ConsoleMessageLevel::kWarning,
      "Form submission canceled because the form is not connected"));
    return;
}

if (is_constructing_entry_list_) {
    GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
      mojom::ConsoleMessageSource::kJavaScript,
      mojom::ConsoleMessageLevel::kWarning,
      "Form submission canceled because the form is "
      "constructing entry list"));
    return;
}

if (is_submitting_)
    return;

// Delay dispatching 'close' to dialog until done submitting.
EventQueueScope scope_for_dialog_close;
base::AutoReset<bool> submit_scope(&is_submitting_, true);

if (event && !submit_button) {
    // In a case of implicit submission without a submit button, 'submit'
    // event handler might add a submit button. We search for a submit
    // button again.
    // TODO(tkent): Do we really need to activate such submit button?
    for (ListedElement* listed_element : ListedElements()) {
      auto* control = DynamicTo<HTMLFormControlElement>(listed_element);
      if (!control)
      continue;
      DCHECK(!control->IsActivatedSubmit());
      if (control->IsSuccessfulSubmitButton()) {
      submit_button = control;
      break;
      }
    }
}

FormSubmission* form_submission =
      FormSubmission::Create(this, attributes_, event, submit_button);
if (!form_submission) {
    // Form submission is not allowed for some NavigationPolicies, e.g. Link
    // Preview. If an user triggered such user event for form submission, just
    // ignores it.
    return;
}
Frame* target_frame = form_submission->TargetFrame();

// 'formdata' event handlers might disconnect the form.
if (!isConnected()) {
    GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
      mojom::ConsoleMessageSource::kJavaScript,
      mojom::ConsoleMessageLevel::kWarning,
      "Form submission canceled because the form is not connected"));
    return;
}

if (form_submission->Method() == FormSubmission::kDialogMethod) {
    SubmitDialog(form_submission);
    return;
}

DCHECK(form_submission->Method() == FormSubmission::kPostMethod ||
         form_submission->Method() == FormSubmission::kGetMethod);
DCHECK(form_submission->Data());
if (form_submission->Action().IsEmpty())
    return;
if (GetExecutionContext()->IsSandboxed(
          network::mojom::blink::WebSandboxFlags::kForms)) {
    // FIXME: This message should be moved off the console once a solution to
    // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
    GetExecutionContext()->AddConsoleMessage(
      MakeGarbageCollected<ConsoleMessage>(
            mojom::blink::ConsoleMessageSource::kSecurity,
            mojom::blink::ConsoleMessageLevel::kError,
            "Blocked form submission to '" +
                form_submission->Action().ElidedString() +
                "' because the form's frame is sandboxed and the 'allow-forms' "
                "permission is not set."));
    return;
}

if (form_submission->Action().ProtocolIsJavaScript()) {
    // For javascript URLs we need to do the CSP check for 'form-action' here.
    // All other schemes are checked in the browser.
    //
    // TODO(antoniosartori): Should we keep the 'form-action' check for
    // javascript: URLs? For 'frame-src' and 'navigate-to', we do not check
    // javascript: URLs. Reading the specification, it looks like 'form-action'
    // should not apply to javascript: URLs.
    if (!GetExecutionContext()->GetContentSecurityPolicy()->AllowFormAction(
            form_submission->Action())) {
      return;
    }
}

UseCounter::Count(GetDocument(), WebFeature::kFormsSubmitted);
if (MixedContentChecker::IsMixedFormAction(GetDocument().GetFrame(),
                                             form_submission->Action())) {
    UseCounter::Count(GetDocument(), WebFeature::kMixedContentFormsSubmitted);
}
if (FastHasAttribute(html_names::kDisabledAttr)) {
    UseCounter::Count(GetDocument(),
                      WebFeature::kFormDisabledAttributePresentAndSubmit);
}

if (!target_frame)
    return;

if (form_submission->Action().ProtocolIsJavaScript()) {
    // For javascript urls, don't post a task to execute the form submission
    // because we already get another task posted for it in
    // Document::ProcessJavascriptUrl. If we post two tasks, the javascript will
    // be run too late according to some tests.
    form_submission->Navigate();
    return;
}

FrameScheduler* scheduler = GetDocument().GetFrame()->GetFrameScheduler();

if (auto* target_local_frame = DynamicTo<LocalFrame>(target_frame)) {
    if (!target_local_frame->IsNavigationAllowed())
      return;

    // Cancel parsing if the form submission is targeted at this frame.
    if (target_local_frame == GetDocument().GetFrame() &&
      !form_submission->Action().ProtocolIsJavaScript()) {
      target_local_frame->GetDocument()->CancelParsing();
    }

    // Use the target frame's frame scheduler. If we can't due to targeting a
    // RemoteFrame, then use the frame scheduler from the frame this form is in.
    scheduler = target_local_frame->GetFrameScheduler();

    // Cancel pending javascript url navigations for the target frame. This new
    // form submission should take precedence over them.
    target_local_frame->GetDocument()->CancelPendingJavaScriptUrls();

    // Cancel any pre-existing attempt to navigate the target frame which was
    // already sent to the browser process so this form submission will take
    // precedence over it.
    target_local_frame->Loader().CancelClientNavigation();
}

cancel_last_submission_ =
      target_frame->ScheduleFormSubmission(scheduler, form_submission);
} 3、FormSubmission::Create()函数构建FirstName=Mickey&LastName=Mouse,以及打开提交页面方式:

FormSubmission* FormSubmission::Create(HTMLFormElement* form,
                                       const Attributes& attributes,
                                       const Event* event,
                                       HTMLFormControlElement* submit_button) {
DCHECK(form);

FormSubmission::Attributes copied_attributes;
copied_attributes.CopyFrom(attributes);
if (submit_button) {
    AtomicString attribute_value;
    if (!(attribute_value =
            submit_button->FastGetAttribute(html_names::kFormactionAttr))
             .IsNull())
      copied_attributes.ParseAction(attribute_value);
    if (!(attribute_value =
            submit_button->FastGetAttribute(html_names::kFormenctypeAttr))
             .IsNull())
      copied_attributes.UpdateEncodingType(attribute_value);
    if (!(attribute_value =
            submit_button->FastGetAttribute(html_names::kFormmethodAttr))
             .IsNull())
      copied_attributes.UpdateMethodType(attribute_value);
    if (!(attribute_value =
            submit_button->FastGetAttribute(html_names::kFormtargetAttr))
             .IsNull())
      copied_attributes.SetTarget(attribute_value);
}

if (copied_attributes.Method() == kDialogMethod) {
    if (submit_button) {
      return MakeGarbageCollected<FormSubmission>(
          submit_button->ResultForDialogSubmit());
    }
    return MakeGarbageCollected<FormSubmission>("");
}

Document& document = form->GetDocument();
KURL action_url = document.CompleteURL(copied_attributes.Action().empty()
                                             ? document.Url().GetString()
                                             : copied_attributes.Action());

if ((document.domWindow()->GetSecurityContext().GetInsecureRequestPolicy() &
       mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) !=
          mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone &&
      action_url.ProtocolIs("http") &&
      !network::IsUrlPotentiallyTrustworthy(GURL(action_url))) {
    UseCounter::Count(document,
                      WebFeature::kUpgradeInsecureRequestsUpgradedRequestForm);
    action_url.SetProtocol("https");
    if (action_url.Port() == 80)
      action_url.SetPort(443);
}

bool is_mailto_form = action_url.ProtocolIs("mailto");
bool is_multi_part_form = false;
AtomicString encoding_type = copied_attributes.EncodingType();

if (copied_attributes.Method() == kPostMethod) {
    is_multi_part_form = copied_attributes.IsMultiPartForm();
    if (is_multi_part_form && is_mailto_form) {
      encoding_type = AtomicString("application/x-www-form-urlencoded");
      is_multi_part_form = false;
    }
}
WTF::TextEncoding data_encoding =
      is_mailto_form
          ? UTF8Encoding()
          : FormDataEncoder::EncodingFromAcceptCharset(
                copied_attributes.AcceptCharset(), document.Encoding());
//解析<input>构建请求参数FirstName=Mickey&LastName=Mouse
FormData* dom_form_data = form->ConstructEntryList(
      submit_button, data_encoding.EncodingForFormSubmission());
DCHECK(dom_form_data);

scoped_refptr<EncodedFormData> form_data;
String boundary;

if (is_multi_part_form) {
    form_data = dom_form_data->EncodeMultiPartFormData();
    boundary = form_data->Boundary().data();
} else {
    form_data = dom_form_data->EncodeFormData(
      attributes.Method() == kGetMethod
            ? EncodedFormData::kFormURLEncoded
            : EncodedFormData::ParseEncodingType(encoding_type));
    if (copied_attributes.Method() == kPostMethod && is_mailto_form) {
      // Convert the form data into a string that we put into the URL.
      AppendMailtoPostFormDataToURL(action_url, *form_data, encoding_type);
      form_data = EncodedFormData::Create();
    }
}

form_data->SetIdentifier(GenerateFormDataIdentifier());
form_data->SetContainsPasswordData(dom_form_data->ContainsPasswordData());

if (copied_attributes.Method() != FormSubmission::kPostMethod &&
      !action_url.ProtocolIsJavaScript()) {
    action_url.SetQuery(form_data->FlattenToString());
}

std::unique_ptr<ResourceRequest> resource_request =
      std::make_unique<ResourceRequest>(action_url);
ClientNavigationReason reason = ClientNavigationReason::kFormSubmissionGet;
if (copied_attributes.Method() == FormSubmission::kPostMethod) {
    reason = ClientNavigationReason::kFormSubmissionPost;
    resource_request->SetHttpMethod(http_names::kPOST);
    resource_request->SetHttpBody(form_data);

    // construct some user headers if necessary
    if (boundary.empty()) {
      resource_request->SetHTTPContentType(encoding_type);
    } else {
      resource_request->SetHTTPContentType(encoding_type +
                                           "; boundary=" + boundary);
    }
}
resource_request->SetHasUserGesture(
      LocalFrame::HasTransientUserActivation(form->GetDocument().GetFrame()));
resource_request->SetFormSubmission(true);

mojom::blink::TriggeringEventInfo triggering_event_info;
if (event) {
    triggering_event_info =
      event->isTrusted()
            ? mojom::blink::TriggeringEventInfo::kFromTrustedEvent
            : mojom::blink::TriggeringEventInfo::kFromUntrustedEvent;
    if (event->UnderlyingEvent())
      event = event->UnderlyingEvent();
} else {
    triggering_event_info = mojom::blink::TriggeringEventInfo::kNotFromEvent;
}

FrameLoadRequest frame_request(form->GetDocument().domWindow(),
                                 *resource_request);
//设置submit打开策略
NavigationPolicy navigation_policy = NavigationPolicyFromEvent(event);
if (navigation_policy == kNavigationPolicyLinkPreview) {
    return nullptr;
}
frame_request.SetNavigationPolicy(navigation_policy);
frame_request.SetClientRedirectReason(reason);
if (submit_button) {
    frame_request.SetSourceElement(submit_button);
} else {
    frame_request.SetSourceElement(form);
}
frame_request.SetTriggeringEventInfo(triggering_event_info);
AtomicString target_or_base_target = frame_request.CleanNavigationTarget(
      copied_attributes.Target().empty() ? document.BaseTarget()
                                       : copied_attributes.Target());

if (form->HasRel(HTMLFormElement::kNoReferrer)) {
    frame_request.SetNoReferrer();
    frame_request.SetNoOpener();
}
if (form->HasRel(HTMLFormElement::kNoOpener) ||
      (EqualIgnoringASCIICase(target_or_base_target, "_blank") &&
       !form->HasRel(HTMLFormElement::kOpener) &&
       form->GetDocument()
         .domWindow()
         ->GetFrame()
         ->GetSettings()
         ->GetTargetBlankImpliesNoOpenerEnabledWillBeRemoved())) {
    frame_request.SetNoOpener();
}
//给主进程发送message.set_method_name("CreateNewWindow");
Frame* target_frame =
      form->GetDocument()
          .GetFrame()
          ->Tree()
          .FindOrCreateFrameForNavigation(frame_request, target_or_base_target)
          .frame;

// Apply replacement now, before any async steps, as the result may change.
WebFrameLoadType load_type = WebFrameLoadType::kStandard;
LocalFrame* target_local_frame = DynamicTo<LocalFrame>(target_frame);
if (target_local_frame &&
      target_local_frame->NavigationShouldReplaceCurrentHistoryEntry(
          frame_request, load_type)) {
    load_type = WebFrameLoadType::kReplaceCurrentItem;
}

return MakeGarbageCollected<FormSubmission>(
      copied_attributes.Method(), action_url, target_or_base_target,
      encoding_type, frame_request.GetSourceElement(), std::move(form_data),
      event, frame_request.GetNavigationPolicy(), triggering_event_info, reason,
      std::move(resource_request), target_frame, load_type,
      form->GetDocument().domWindow(),
      form->GetDocument().GetFrame()->GetLocalFrameToken(),
      CaptureSourceLocation(form->GetDocument().domWindow()),
      form->GetDocument()
          .domWindow()
          ->GetPolicyContainer()
          ->IssueKeepAliveHandle());
}  3.1)、构建FirstName=Mickey&LastName=Mouse 参数

/ /ConstructEntryList函数遍历form表单位素
  FormData* dom_form_data = form->ConstructEntryList(
      submit_button, data_encoding.EncodingForFormSubmission());
  DCHECK(dom_form_data);
3.2)、FormSubmission::Create调用FindOrCreateFrameForNavigation

用来打开提交新页面。
  Frame* target_frame =
      form->GetDocument()
          .GetFrame()
          ->Tree()
          .FindOrCreateFrameForNavigation(frame_request, target_or_base_target)
          .frame;
https://i-blog.csdnimg.cn/direct/4b684d2e5399420c91603315edb34cb5.png
3.3)、FindOrCreateFrameForNavigation调用RenderFrameImpl::CreateNewWindow 函数

RenderFrameImpl::CreateNewWindow函数定义(content\renderer\render_frame_impl.cc):
WebView* RenderFrameImpl::CreateNewWindow(
    const WebURLRequest& request,
    const blink::WebWindowFeatures& features,
    const WebString& frame_name,
    WebNavigationPolicy policy,
    network::mojom::WebSandboxFlags sandbox_flags,
    const blink::SessionStorageNamespaceId& session_storage_namespace_id,
    bool& consumed_user_gesture,
    const absl::optional<blink::Impression>& impression,
    const absl::optional<blink::WebPictureInPictureWindowOptions>& pip_options,
    const blink::WebURL& base_url) {
consumed_user_gesture = false;
mojom::CreateNewWindowParamsPtr params = mojom::CreateNewWindowParams::New();

// The user activation check is done at the browser process through
// |frame_host->CreateNewWindow()| call below.But the extensions case
// handled through the following |if| is an exception.
params->allow_popup = false;
if (GetContentClient()->renderer()->AllowPopup())
    params->allow_popup = true;

params->window_container_type = WindowFeaturesToContainerType(features);

params->session_storage_namespace_id = session_storage_namespace_id;
if (!features.noopener) {
    params->clone_from_session_storage_namespace_id =
      GetWebView()->GetSessionStorageNamespaceId();
}

const std::string& frame_name_utf8 = frame_name.Utf8(
      WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
params->frame_name = frame_name_utf8;
params->opener_suppressed = features.noopener;
params->disposition = NavigationPolicyToDisposition(policy);
if (!request.IsNull()) {
    params->target_url = request.Url();
    // The browser process does not consider empty URLs as valid (partly due to
    // a risk of treating them as a navigation to the privileged NTP in some
    // cases), so treat an attempt to create a window with an empty URL as
    // opening about:blank.
    //
    // Similarly, javascript: URLs should not be sent to the browser process,
    // since they are either handled within the renderer process (if a window is
    // created within the same browsing context group) or ignored (in the
    // noopener case). Use about:blank for the URL in that case as well, to
    // reduce the risk of running them incorrectly.
    if (params->target_url.is_empty() ||
      params->target_url.SchemeIs(url::kJavaScriptScheme)) {
      params->target_url = GURL(url::kAboutBlankURL);
    }

    params->referrer = blink::mojom::Referrer::New(
      blink::WebStringToGURL(request.ReferrerString()),
      request.GetReferrerPolicy());
}
params->features = ConvertWebWindowFeaturesToMojoWindowFeatures(features);

params->is_form_submission = request.IsFormSubmission();
params->form_submission_post_data =
      blink::GetRequestBodyForWebURLRequest(request);
params->form_submission_post_content_type = request.HttpContentType().Utf8();

params->impression = impression;

if (pip_options) {
    CHECK_EQ(policy, blink::kWebNavigationPolicyPictureInPicture);
    auto pip_mojom_opts = blink::mojom::PictureInPictureWindowOptions::New();
    pip_mojom_opts->width = pip_options->width;
    pip_mojom_opts->height = pip_options->height;
    // TODO(crbug.com/1444658): Remove this from mojom and the browser side.
    pip_mojom_opts->initial_aspect_ratio = 0.0;
    // TODO(crbug.com/1410379): Remove this from mojom and the browser side.
    pip_mojom_opts->lock_aspect_ratio = false;
    params->pip_options = std::move(pip_mojom_opts);
}

params->download_policy.ApplyDownloadFramePolicy(
      /*is_opener_navigation=*/false, request.HasUserGesture(),
      // `openee_can_access_opener_origin` only matters for opener navigations,
      // so its value here is irrelevant.
      /*openee_can_access_opener_origin=*/true,
      !GetWebFrame()->IsAllowedToDownload(), GetWebFrame()->IsAdFrame());

params->initiator_activation_and_ad_status =
      blink::GetNavigationInitiatorActivationAndAdStatus(
          request.HasUserGesture(), GetWebFrame()->IsAdFrame(),
          GetWebFrame()->IsAdScriptInStack());

// We preserve this information before sending the message since |params| is
// moved on send.
bool is_background_tab =
      params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB;

mojom::CreateNewWindowStatus status;
mojom::CreateNewWindowReplyPtr reply;
auto* frame_host = GetFrameHost();
if (!frame_host->CreateNewWindow(std::move(params), &status, &reply)) {
    // The sync IPC failed, e.g. maybe the render process is in the middle of
    // shutting down. Can't create a new window without the browser process,
    // so just bail out.
    return nullptr;
}

// If creation of the window was blocked (e.g. because this frame doesn't
// have user activation), return before consuming user activation. A frame
// that isn't allowed to open a windowshouldn't be able to consume the
// activation for the rest of the frame tree.
if (status == mojom::CreateNewWindowStatus::kBlocked)
    return nullptr;

// For Android WebView, we support a pop-up like behavior for window.open()
// even if the embedding app doesn't support multiple windows. In this case,
// window.open() will return "window" and navigate it to whatever URL was
// passed. We also don't need to consume user gestures to protect against
// multiple windows being opened, because, well, the app doesn't support
// multiple windows.
// TODO(dcheng): It's awkward that this is plumbed into Blink but not really
// used much in Blink, except to enable web testing... perhaps this should
// be checked directly in the browser side.
if (status == mojom::CreateNewWindowStatus::kReuse) {
    // In this case, treat javascript: URLs as blocked rather than running them
    // in a reused main frame in Android WebView. See https://crbug.com/1083819.
    if (!request.IsNull() && request.Url().ProtocolIs(url::kJavaScriptScheme)) {
      return nullptr;
    }
    return GetWebView();
}

// Consume the transient user activation in the current renderer.
consumed_user_gesture = GetWebFrame()->ConsumeTransientUserActivation(
      blink::UserActivationUpdateSource::kBrowser);

// If we should ignore the new window (e.g. because of `noopener`), return
// now that user activation was consumed.
if (status == mojom::CreateNewWindowStatus::kIgnore)
    return nullptr;

DCHECK(reply);
DCHECK_NE(MSG_ROUTING_NONE, reply->main_frame_route_id);
DCHECK_NE(MSG_ROUTING_NONE, reply->widget_params->routing_id);

// While this view may be a background extension page, it can spawn a visible
// render view. So we just assume that the new one is not another background
// page instead of passing on our own value.
// TODO(vangelis): Can we tell if the new view will be a background page?
bool never_composited = false;

// The initial hidden state for the RenderViewImpl here has to match what the
// browser will eventually decide for the given disposition. Since we have to
// return from this call synchronously, we just have to make our best guess
// and rely on the browser sending a WasHidden / WasShown message if it
// disagrees.
mojom::CreateViewParamsPtr view_params = mojom::CreateViewParams::New();

view_params->opener_frame_token = GetWebFrame()->GetFrameToken();
view_params->window_was_opened_by_another_window = true;
view_params->renderer_preferences = GetWebView()->GetRendererPreferences();
view_params->web_preferences = GetWebView()->GetWebPreferences();

view_params->replication_state = blink::mojom::FrameReplicationState::New();
view_params->replication_state->frame_policy.sandbox_flags = sandbox_flags;
view_params->replication_state->name = frame_name_utf8;
view_params->devtools_main_frame_token = reply->devtools_main_frame_token;
view_params->browsing_context_group_info = reply->browsing_context_group_info;
view_params->color_provider_colors = reply->color_provider_colors;

auto main_frame_params = mojom::CreateLocalMainFrameParams::New();
main_frame_params->frame_token = reply->main_frame_token;
main_frame_params->routing_id = reply->main_frame_route_id;
main_frame_params->frame = std::move(reply->frame);
main_frame_params->interface_broker =
      std::move(reply->main_frame_interface_broker);
main_frame_params->document_token = reply->document_token;
main_frame_params->policy_container = std::move(reply->policy_container);
main_frame_params->associated_interface_provider_remote =
      std::move(reply->associated_interface_provider);
main_frame_params->widget_params = std::move(reply->widget_params);
main_frame_params->subresource_loader_factories =
      base::WrapUnique(static_cast<blink::PendingURLLoaderFactoryBundle*>(
          CloneLoaderFactories()->Clone().release()));

view_params->main_frame =
      mojom::CreateMainFrameUnion::NewLocalParams(std::move(main_frame_params));
view_params->blink_page_broadcast = std::move(reply->page_broadcast);
view_params->session_storage_namespace_id =
      reply->cloned_session_storage_namespace_id;
DCHECK(!view_params->session_storage_namespace_id.empty())
      << "Session storage namespace must be populated.";
view_params->hidden = is_background_tab;
view_params->never_composited = never_composited;

WebView* web_view = agent_scheduling_group_->CreateWebView(
      std::move(view_params),
      /*was_created_by_renderer=*/true, base_url);

if (reply->wait_for_debugger) {
    blink::WebFrameWidget* frame_widget =
      web_view->MainFrame()->ToWebLocalFrame()->LocalRoot()->FrameWidget();
    frame_widget->WaitForDebuggerWhenShown();
}

return web_view;
} 堆栈图:
https://i-blog.csdnimg.cn/direct/bdbb0f8f46014671a301b43d39026e2b.png
@3        content.dll!content::RenderFrameImpl::CreateNewWindow(const blink::WebURLRequest & request, const blink::WebWindowFeatures & features, const blink::WebString & frame_name, blink::WebNavigationPolicy policy, network::mojom::WebSandboxFlags sandbox_flags, const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & session_storage_namespace_id, bool & consumed_user_gesture, const std::__Cr::optional<blink::Impression> & impression, const std::__Cr::optional<blink::WebPictureInPictureWindowOptions> & pip_options, const blink::WebURL & base_url) 行 6562        C++        已加载符号。

@2         blink_core.dll!blink::ChromeClientImpl::CreateWindowDelegate(blink::LocalFrame * frame, const blink::FrameLoadRequest & r, const WTF::AtomicString & name, const blink::WebWindowFeatures & features, network::mojom::WebSandboxFlags sandbox_flags, const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & session_storage_namespace_id, bool & consumed_user_gesture) 行 342        C++        已加载符号。

@1        blink_core.dll!blink::ChromeClient::CreateWindow(blink::LocalFrame * frame, const blink::FrameLoadRequest & r, const WTF::AtomicString & frame_name, const blink::WebWindowFeatures & features, network::mojom::WebSandboxFlags sandbox_flags, const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & session_storage_namespace_id, bool & consumed_user_gesture) 行 88        C++        已加载符号。

@0        blink_core.dll!blink::CreateNewWindow(blink::LocalFrame & opener_frame, blink::FrameLoadRequest & request, const WTF::AtomicString & frame_name) 行 355        C++        已加载符号。
>        blink_core.dll!blink::FrameTree::FindOrCreateFrameForNavigation(blink::FrameLoadRequest & request, const WTF::AtomicString & name) 行 217        C++        已加载符号。 3.4)、RenderFrameImpl::CreateNewWindow 调用frame_host->CreateNewWindow 给主历程发送CreateNewWindow的mojom消息。

  mojom::CreateNewWindowStatus status;
  mojom::CreateNewWindowReplyPtr reply;
  auto* frame_host = GetFrameHost();
 //给主历程发送CreateNewWindow mojom消息
  if (!frame_host->CreateNewWindow(std::move(params), &status, &reply)) {
    // The sync IPC failed, e.g. maybe the render process is in the middle of
    // shutting down. Can't create a new window without the browser process,
    // so just bail out.
    return nullptr;
  }
https://i-blog.csdnimg.cn/direct/727f121ed8714fc48244d7079b4edc66.png
4、主历程收到子历程发送的mojom CreateNewWindow消息:

在RenderFrameHostImpl::CreateNewWindow函数内里处理打开 submit提交URL="https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse"
 src\content\browser\renderer_host\render_frame_host_impl.cc
浏览器打开了一个submit提交的新标签,至此form提交完成。
https://i-blog.csdnimg.cn/direct/0fd47775f8a84c56bb90453482479871.png
void RenderFrameHostImpl::CreateNewWindow(
    mojom::CreateNewWindowParamsPtr params,
    CreateNewWindowCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT2("navigation", "RenderFrameHostImpl::CreateNewWindow",
               "render_frame_host", this, "url", params->target_url);

// Only top-most frames can open picture-in-picture windows.
if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE &&
      GetParentOrOuterDocumentOrEmbedder()) {
    frame_host_associated_receiver_.ReportBadMessage(
      "Only top-most frames can open picture-in-picture windows.");
    return;
}

bool no_javascript_access = false;

// Filter out URLs to which navigation is disallowed from this context.
GetProcess()->FilterURL(false, &params->target_url);

bool effective_transient_activation_state =
      params->allow_popup || HasTransientUserActivation() ||
      (transient_allow_popup_.IsActive() &&
       params->disposition == WindowOpenDisposition::NEW_POPUP);

// Ignore window creation when sent from a frame that's not active or
// created.
bool can_create_window =
      IsActive() && is_render_frame_created() &&
      GetContentClient()->browser()->CanCreateWindow(
          this, GetLastCommittedURL(),
          GetOutermostMainFrame()->GetLastCommittedURL(),
          last_committed_origin_, params->window_container_type,
          params->target_url, params->referrer.To<Referrer>(),
          params->frame_name, params->disposition, *params->features,
          effective_transient_activation_state, params->opener_suppressed,
          &no_javascript_access);

// If this frame isn't allowed to create a window, return early (before we
// consume transient user activation).
if (!can_create_window) {
    std::move(callback).Run(mojom::CreateNewWindowStatus::kBlocked, nullptr);
    return;
}

// Otherwise, consume user activation before we proceed. In particular, it is
// important to do this before we return from the |opener_suppressed| case
// below.
// NB: This call will consume activations in the browser and the remote frame
// proxies for this frame. The initiating renderer will consume its view of
// the activations after we return.

// See `owner_` invariants about `IsActive()`, which is implied by
// `can_create_window`.
CHECK(owner_);
bool was_consumed = owner_->UpdateUserActivationState(
      blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
      blink::mojom::UserActivationNotificationType::kNone);

// For Android WebView, we support a pop-up like behavior for window.open()
// even if the embedding app doesn't support multiple windows. In this case,
// window.open() will return "window" and navigate it to whatever URL was
// passed.
if (!GetOrCreateWebPreferences().supports_multiple_windows) {
    std::move(callback).Run(mojom::CreateNewWindowStatus::kReuse, nullptr);
    return;
}

// This will clone the sessionStorage for namespace_id_to_clone.
StoragePartition* storage_partition = GetStoragePartition();
DOMStorageContextWrapper* dom_storage_context =
      static_cast<DOMStorageContextWrapper*>(
          storage_partition->GetDOMStorageContext());

scoped_refptr<SessionStorageNamespaceImpl> cloned_namespace;
if (!params->clone_from_session_storage_namespace_id.empty()) {
    cloned_namespace = SessionStorageNamespaceImpl::CloneFrom(
      dom_storage_context, params->session_storage_namespace_id,
      params->clone_from_session_storage_namespace_id);
} else {
    cloned_namespace = SessionStorageNamespaceImpl::Create(
      dom_storage_context, params->session_storage_namespace_id);
}

if (IsCredentialless() || IsNestedWithinFencedFrame() ||
      CoopSuppressOpener(/*opener=*/this)) {
    params->opener_suppressed = true;
    // TODO(https://crbug.com/1060691) This should be applied to all
    // popups opened with noopener.
    params->frame_name.clear();
}

RenderFrameHostImpl* top_level_opener = GetMainFrame();
int popup_virtual_browsing_context_group =
      params->opener_suppressed
          ? CrossOriginOpenerPolicyAccessReportManager::
                GetNewVirtualBrowsingContextGroup()
          : top_level_opener->virtual_browsing_context_group();
int popup_soap_by_default_virtual_browsing_context_group =
      params->opener_suppressed
          ? CrossOriginOpenerPolicyAccessReportManager::
                GetNewVirtualBrowsingContextGroup()
          : top_level_opener->soap_by_default_virtual_browsing_context_group();

// If the opener is suppressed or script access is disallowed, we should
// open the window in a new BrowsingInstance, and thus a new process. That
// means the current renderer process will not be able to route messages to
// it. Because of this, we will immediately show and navigate the window
// in OnCreateNewWindowOnUI, using the params provided here.
bool is_new_browsing_instance =
      params->opener_suppressed || no_javascript_access;

DCHECK(IsRenderFrameLive());

// The non-owning pointer |new_frame_tree| is valid in this stack frame since
// nothing can delete it until this thread is freed up again.
FrameTree* new_frame_tree =
      delegate_->CreateNewWindow(this, *params, is_new_browsing_instance,
                                 was_consumed, cloned_namespace.get());

transient_allow_popup_.Deactivate();

MaybeRecordAdClickMainFrameNavigationUseCounter(
      /*initiator_frame=*/this, params->initiator_activation_and_ad_status);

if (is_new_browsing_instance || !new_frame_tree) {
    // Opener suppressed, Javascript access disabled, or delegate did not
    // provide a handle to any windows it created. In these cases, never tell
    // the renderer about the new window.
    std::move(callback).Run(mojom::CreateNewWindowStatus::kIgnore, nullptr);
    return;
}

DCHECK(!params->opener_suppressed);

RenderFrameHostImpl* new_main_rfh =
      new_frame_tree->root()->current_frame_host();

new_main_rfh->virtual_browsing_context_group_ =
      popup_virtual_browsing_context_group;
new_main_rfh->soap_by_default_virtual_browsing_context_group_ =
      popup_soap_by_default_virtual_browsing_context_group;

// COOP and COOP reporter are inherited from the opener to the popup's initial
// empty document.
if (IsOpenerSameOriginFrame(/*opener=*/this) &&
      GetMainFrame()->coop_access_report_manager()->coop_reporter()) {
    new_main_rfh->SetCrossOriginOpenerPolicyReporter(
      std::make_unique<CrossOriginOpenerPolicyReporter>(
            GetProcess()->GetStoragePartition(), GetLastCommittedURL(),
            params->referrer->url,
            // TODO(https://crbug.com/1385827): See if we need to send the
            // origin to reporters as well.
            new_main_rfh->cross_origin_opener_policy(), GetReportingSource(),
            isolation_info_.network_anonymization_key()));
}

mojo::PendingAssociatedRemote<mojom::Frame> pending_frame_remote;
mojo::PendingAssociatedReceiver<mojom::Frame> pending_frame_receiver =
      pending_frame_remote.InitWithNewEndpointAndPassReceiver();
new_main_rfh->SetMojomFrameRemote(std::move(pending_frame_remote));

mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
      browser_interface_broker;
new_main_rfh->BindBrowserInterfaceBrokerReceiver(
      browser_interface_broker.InitWithNewPipeAndPassReceiver());

mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
      pending_associated_interface_provider;
new_main_rfh->BindAssociatedInterfaceProviderReceiver(
      pending_associated_interface_provider
          .InitWithNewEndpointAndPassReceiver());

// With this path, RenderViewHostImpl::CreateRenderView is never called
// because `blink::WebView` is already created on the renderer side. Thus we
// need to establish the connection here.
mojo::PendingAssociatedRemote<blink::mojom::PageBroadcast> page_broadcast;
mojo::PendingAssociatedReceiver<blink::mojom::PageBroadcast>
      page_broadcast_receiver =
          page_broadcast.InitWithNewEndpointAndPassReceiver();

auto widget_params =
      new_main_rfh->GetLocalRenderWidgetHost()
          ->BindAndGenerateCreateFrameWidgetParamsForNewWindow();

new_main_rfh->render_view_host()->BindPageBroadcast(
      std::move(page_broadcast));

bool wait_for_debugger =
      devtools_instrumentation::ShouldWaitForDebuggerInWindowOpen();

mojom::CreateNewWindowReplyPtr reply = mojom::CreateNewWindowReply::New(
      new_main_rfh->GetFrameToken(), new_main_rfh->GetRoutingID(),
      std::move(pending_frame_receiver), std::move(widget_params),
      std::move(page_broadcast_receiver), std::move(browser_interface_broker),
      std::move(pending_associated_interface_provider), cloned_namespace->id(),
      new_main_rfh->GetDevToolsFrameToken(), wait_for_debugger,
      new_main_rfh->GetDocumentToken(),
      new_main_rfh->policy_container_host()->CreatePolicyContainerForBlink(),
      blink::BrowsingContextGroupInfo(
          new_main_rfh->GetSiteInstance()->browsing_instance_token(),
          new_main_rfh->GetSiteInstance()->coop_related_group_token()),
      delegate_->GetColorProviderColorMaps());

std::move(callback).Run(mojom::CreateNewWindowStatus::kSuccess,
                        std::move(reply));

// The mojom reply callback with kSuccess causes the renderer to create the
// renderer-side objects.
new_main_rfh->render_view_host()->RenderViewCreated(new_main_rfh);
} 子历程发送消息定义在
F:\code\google\src\out\Debug\gen\content\common\frame.mojom.cc
message.set_method_name("CreateNewWindow");
FrameHostProxy::FrameHostProxy(mojo::MessageReceiverWithResponder* receiver)
    : receiver_(receiver) {
}
bool FrameHostProxy::CreateNewWindow(
    CreateNewWindowParamsPtr param_params, CreateNewWindowStatus* out_param_status, CreateNewWindowReplyPtr* out_param_reply) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
TRACE_EVENT_BEGIN1(
    "mojom", "Call content::mojom::FrameHost::CreateNewWindow (sync)", "input_parameters",
    [&](perfetto::TracedValue context){
      auto dict = std::move(context).WriteDictionary();
      perfetto::WriteIntoTracedValueWithFallback(
         dict.AddItem("params"), param_params,
                        "<value of type CreateNewWindowParamsPtr>");
   });
#else
TRACE_EVENT0("mojom", "FrameHost::CreateNewWindow");
#endif

const bool kExpectsResponse = true;
const bool kIsSync = true;
const bool kAllowInterrupt =
      true;
const bool is_urgent = false;

const uint32_t kFlags =
      ((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |
      ((kIsSync) ? mojo::Message::kFlagIsSync : 0) |
      ((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) |
      ((is_urgent) ? mojo::Message::kFlagIsUrgent : 0);

mojo::Message message(
      internal::kFrameHost_CreateNewWindow_Name, kFlags, 0, 0, nullptr);
mojo::internal::MessageFragment<
      ::content::mojom::internal::FrameHost_CreateNewWindow_Params_Data> params(
          message);
params.Allocate();
mojo::internal::MessageFragment<
      typename decltype(params->params)::BaseType> params_fragment(
          params.message());
mojo::internal::Serialize<::content::mojom::CreateNewWindowParamsDataView>(
      param_params, params_fragment);
params->params.Set(
      params_fragment.is_null() ? nullptr : params_fragment.data());
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
      params->params.is_null(),
      mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
      "null params in FrameHost.CreateNewWindow request");

#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name(FrameHost::Name_);
message.set_method_name("CreateNewWindow");
#endif

bool result = false;
std::unique_ptr<mojo::MessageReceiver> responder(
      new FrameHost_CreateNewWindow_HandleSyncResponse(
          &result, out_param_status, out_param_reply));
::mojo::internal::SendMojoMessage(*receiver_, message, std::move(responder));
#if BUILDFLAG(MOJO_TRACE_ENABLED)
TRACE_EVENT_END1(
    "mojom", "FrameHost::CreateNewWindow", "sync_response_parameters",
    [&](perfetto::TracedValue context){
      auto dict = std::move(context).WriteDictionary();
      perfetto::WriteIntoTracedValueWithFallback(
         dict.AddItem("status"), out_param_status,
                        "<value of type CreateNewWindowStatus>");
      perfetto::WriteIntoTracedValueWithFallback(
         dict.AddItem("reply"), out_param_reply,
                        "<value of type CreateNewWindowReplyPtr>");
   });
#endif
return result;
} 总结:至此分析完毕。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Chromium 前端form表单提交过程分析c++