当前位置:首页 » 《我的小黑屋》 » 正文

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

9 人参与  2024年10月19日 17:22  分类 : 《我的小黑屋》  评论

点击全文阅读


一、本文以一个简单的 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 {    [CEReactions, Reflect=accept_charset] attribute DOMString acceptCharset;    [CEReactions, URL] attribute USVString action;    [CEReactions, Reflect, ReflectOnly=("on","off"), ReflectMissing="on", ReflectInvalid="on"] attribute DOMString autocomplete;    [CEReactions] attribute DOMString enctype;    [CEReactions] attribute DOMString encoding;    [CEReactions] attribute DOMString method;    [CEReactions, Reflect] attribute DOMString name;    [CEReactions, Reflect] attribute boolean noValidate;    [CEReactions, Reflect] attribute DOMString target;    [CEReactions, Reflect] attribute DOMString rel;    [SameObject, PutForwards=value] readonly attribute DOMTokenList relList;    readonly attribute HTMLFormControlsCollection elements;    readonly attribute long length;    [ImplementedAs=item] getter Element (unsigned long index);    // FIXME: This getter should not have [NotEnumerable].    [NotEnumerable] getter (RadioNodeList or Element) (DOMString name);    [ImplementedAs=submitFromJavaScript] void submit();    [RaisesException] void requestSubmit(optional HTMLElement? submitter = null);    [CEReactions] 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);// [CEReactions]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[0];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-formdatatypedef (File or USVString) FormDataEntryValue;[    Exposed=(Window,Worker)] interface FormData {    [RaisesException] constructor(optional HTMLFormElement form, optional HTMLElement? submitter = null);    void append(USVString name, USVString value);    [CallWith=ScriptState] void append(USVString name, Blob value, optional USVString filename);    [ImplementedAs=deleteEntry] 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();}

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;

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 window  shouldn'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;}

堆栈图:

 @3content.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) 行 6562C++已加载符号。@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) 行 342C++已加载符号。 @1blink_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) 行 88C++已加载符号。 @0blink_core.dll!blink::CreateNewWindow(blink::LocalFrame & opener_frame, blink::FrameLoadRequest & request, const WTF::AtomicString & frame_name) 行 355C++已加载符号。>blink_core.dll!blink::FrameTree::FindOrCreateFrameForNavigation(blink::FrameLoadRequest & request, const WTF::AtomicString & name) 行 217C++已加载符号。

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;
  }

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提交完成。

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;}

总结:

   GET方式,会将表单中的数据(键值对)经过urlencode编码后追加到url中。


点击全文阅读


本文链接:http://zhangshiyu.com/post/174270.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1