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 {
    [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-formdata

typedef (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 参数
  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;
}

堆栈图:

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

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

总结:至此分析完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值