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

news2024/11/23 15:33:10

一、本文以一个简单的 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 参数

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

堆栈图:

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

总结:至此分析完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unreal5从入门到精通之 如何使用事件分发器EventDispather

文章目录 前言1.创建事件分发器设置属性2.创建Bind、Unbind及Unbind All节点在蓝图类中创建在关卡蓝图中创建3.创建事件分发器事件节点4.调用事件分发器在蓝图类中进行调用在关卡蓝图中进行调用精彩推荐前言 事件分发器是 Unreal Engine(UE)中一个重要的概念,它负责在游戏运…

【C++】右值引用和移动语义(带你理解C++为何如此高效)

1.右值引用和移动语义 左值和右值的重点区分是能否取地址。 能取地址的是左值&#xff08;可以是值&#xff0c;也可以是表达式&#xff09;&#xff0c;不能取地址的是右值。 1.1 什么是左值 1.2 什么是右值 1.2.1 常见的右值 常见右值&#xff1a;常数&#xff08;10&…

【C/C++】速通某站上的经典“笔试”编程题

【C/C】速通某站上的经典“笔试”编程题 一. 题目描述&#xff1a;解题思路&#xff1a;代码实现&#xff1a; 二. 题目描述&#xff1a;解题思路&#xff1a;代码实现&#xff1a; 三. 题目描述&#xff1a;解题思路&#xff1a;代码实现&#xff1a; 一. 题目描述&#xff1a…

DS线性表之栈的讲解和实现(4)

文章目录 前言一、栈的概念及结构二、关于实现栈的分析关于栈顶指针top关于结构体栈的初始化入栈出栈获取栈顶元素获取栈元素个数判断栈是否为空栈的销毁 总结 前言 栈就是一个比较实用的数据结构了&#xff0c;且大致逻辑就是套用之前的两种线性表 具体选择哪种呢&#xff1f;…

综合布线研究实训室建设方案

1、 引言 随着信息技术的飞速发展&#xff0c;综合布线系统作为信息传输的基础设施&#xff0c;在各类建筑及信息化项目中发挥着越来越重要的作用。为了满足职业院校及企业对综合布线技术人才培养和研究的需求&#xff0c;本方案旨在建设一个集教学、实训、研究于一体的综合布…

ARM base instruction -- smull

有符号乘法运算 Signed Multiply Long multiplies two 32-bit register values, and writes the result to the 64-bit destination register. 将两个32位寄存器值相乘&#xff0c;并将结果写入64位目标寄存器。 64-bit variant SMULL <Xd>, <Wn>, <Wm>…

Linux破解root用户密码

在Linux启动菜单界面按【e】进入编辑启动菜单项 在LANGzh_CN.UTF-8&#xff08;或LANGen_US.UTF-8&#xff09;后面空出一格输入 rd.break consoletty0,再按【ctrlx】键启动Linux系统 以可读写的方式重新挂载文件系统 mount -o remount,rw /sysroot 改变根目录为/sysro…

Attention Is All You Need论文翻译

论文名称 注意力即是全部 论文地址 https://user.phil.hhu.de/~cwurm/wp-content/uploads/2020/01/7181-attention-is-all-you-need.pdf 摘要 主流的序列转导模型基于复杂的递归或卷积神经网络&#xff0c;这些网络包含编码器和解码器。性能最好的模型通过注意力机制将编码器和…

快速学习一个算法,Transformer模型架构

今天给大家分享一个超强的算法模型&#xff0c;Transformer Transformer 模型是目前自然语言处理&#xff08;NLP&#xff09;以及计算机视觉等领域中应用非常广泛的深度学习模型架构。 它由 Vaswani 等人在 2017 年的论文《Attention is All You Need》中提出&#xff0c;并…

【智能大数据分析 | 实验三】Storm实验:实时WordCountTopology

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…

并查集的实现(朴素版)

这是C算法基础-数据结构专栏的第二十九篇文章&#xff0c;专栏详情请见此处。 由于作者即将参加CSP&#xff0c;所以到比赛结束前将不再发表文章&#xff01; 引入 并查集是一种可以快速合并查找集合的一种数据结构&#xff0c;这次我们将通过三道题来详细讲解并查集&#xff…

迈普pnsr2900x DOWNLOAD_FILE 任意文件读取漏洞

0x01 产品描述&#xff1a; ‌ 迈普NSR2900X系列是一款专为军队、政府、金融、中小型企业分支机构和中小型企业总部设计的信创接入路由器。‌ 该路由器采用国产核心元器件&#xff0c;基于国产操作系统运行迈普自主研发的网络操作系统及应用软件。它全面支持IPv4、IPv6、OS…

insert into values 语句优化

insert into values插入单行数据 SQL语句&#xff0c;insert into values插入单行数据&#xff0c;执行10万次&#xff0c;执行时间1279秒&#xff0c;优化总体执行耗时。 SQL文本&#xff0c;单行insert values&#xff0c;没有select部分。需要进一步分析执行过程消耗。 ins…

软考《信息系统运行管理员》- 5.1 信息系统数据资源维护体系

5.1 信息系统数据资源维护体系 文章目录 5.1 信息系统数据资源维护体系数据资源维护的管理对象数据资源维护的管理类型运行监控故障响应数据备份归档检索数据优化 数据资源维护的管理内容维护方案例行管理应急响应数据资源的开发与利用 数据是信息系统管理的对象与结果&#xf…

7-基于国产化FT-M6678+JFM7K325T的6U CPCI信号处理卡

一、板卡概述 本板卡系我公司自主研发&#xff0c;基于6U CPCI的通用高性能信号处理平台。板卡采用一片国产8核DSP FT-C6678和一片国产FPGA JFM7K325T-2FFG900作为主处理器。为您提供了丰富的运算资源。如下图所示&#xff1a; 二、设计参考标准 ● PCIMG 2.0 R3.0 CompactP…

Python酷库之旅-第三方库Pandas(147)

目录 一、用法精讲 666、pandas.Timestamp.astimezone方法 666-1、语法 666-2、参数 666-3、功能 666-4、返回值 666-5、说明 666-6、用法 666-6-1、数据准备 666-6-2、代码示例 666-6-3、结果输出 667、pandas.Timestamp.ceil方法 667-1、语法 667-2、参数 667…

基础篇:带你打开Vue的大门(一)

学习目标&#xff1a; 理解Vue的基本概念&#xff1a;掌握Vue.js是什么&#xff0c;它的设计理念&#xff0c;以及它在现代Web开发中的应用。掌握Vue的基本语法&#xff1a;学习Vue的基础指令和语法&#xff0c;能够使用Vue构建简单的交互式界面。熟悉Vue组件化开发&#xff1…

DBA | 如何将 .bak 的数据库备份文件导入到SQL Server 数据库中?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 原文链接&#xff1a;DBA | 如何将 .bak 的数据库备份文件导入到SQL Server 数据库中? 如何将&#xff08;.bak&#xff09;的SQL Server 数据库备份文件导入到当前数据库中? Step 1.登录到 Sql…

Centos7安装RocketMQ[图文教程]

文章目录 RocketMQ介绍基于Linux服务部署RocketMQ&#xff08;单机&#xff09;配置JDK环境下载RocketMQ部署RocketMQ1、解压2、修改VM参数3、配置环境变量4、编写Service文件5、启动服务 基于Docker方式部署RocketMQ安装Docker编写docker-compose文件启动RocketMQ服务 部署Roc…

前端学习-css的背景(十六)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 背景颜色 语法格式 背景图片 语法格式 背景平铺 语法格式 背景图片位置 语法格式 参数代表的意思 参数是方位名词 参数是精确单位 参数是混合单位 背…