一、本文以一个简单的 HTML 表单,包含两个文本输入框和一个提交按钮:
<form action="demo_form.php"> First name: <input type="text" name="fname"><br> Last name: <input type="text" name="lname"><br> <input type="submit" value="提交"></form>
测试时候可以打开菜鸟教程在线编辑器 (runoob.com)运行以上代码
二、看下c++form表单接口定义:
2.1)、form接口定义文件:
third_party\blink\renderer\core\html\forms\html_form_element.idl
method="get|post|dialog"
// https://html.spec.whatwg.org/C/#the-form-element[ Exposed=Window, HTMLConstructor, LegacyOverrideBuiltIns] interface HTMLFormElement : HTMLElement { [CEReactions, Reflect=accept_charset] attribute DOMString acceptCharset; [CEReactions, URL] attribute USVString action; [CEReactions, Reflect, ReflectOnly=("on","off"), ReflectMissing="on", ReflectInvalid="on"] attribute DOMString autocomplete; [CEReactions] attribute DOMString enctype; [CEReactions] attribute DOMString encoding; [CEReactions] attribute DOMString method; [CEReactions, Reflect] attribute DOMString name; [CEReactions, Reflect] attribute boolean noValidate; [CEReactions, Reflect] attribute DOMString target; [CEReactions, Reflect] attribute DOMString rel; [SameObject, PutForwards=value] readonly attribute DOMTokenList relList; readonly attribute HTMLFormControlsCollection elements; readonly attribute long length; [ImplementedAs=item] getter Element (unsigned long index); // FIXME: This getter should not have [NotEnumerable]. [NotEnumerable] getter (RadioNodeList or Element) (DOMString name); [ImplementedAs=submitFromJavaScript] void submit(); [RaisesException] void requestSubmit(optional HTMLElement? submitter = null); [CEReactions] void reset(); boolean checkValidity(); boolean reportValidity();};
2.2)、blink下form接口实现:
third_party\blink\renderer\core\html\forms\html_form_element.h
third_party\blink\renderer\core\html\forms\html_form_element.cc
用于查找和遍历form元素
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_FORM_ELEMENT_H_#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_FORM_ELEMENT_H_#include "third_party/blink/renderer/core/core_export.h"#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"#include "third_party/blink/renderer/core/html/forms/radio_button_group_scope.h"#include "third_party/blink/renderer/core/html/html_element.h"#include "third_party/blink/renderer/core/loader/form_submission.h"#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"namespace blink {class DOMTokenList;class Event;class HTMLFormControlElement;class HTMLFormControlsCollection;class HTMLImageElement;class ListedElement;class RelList;class V8UnionElementOrRadioNodeList;class CORE_EXPORT HTMLFormElement final : public HTMLElement { DEFINE_WRAPPERTYPEINFO(); public: enum RelAttribute { kNone = 0, kNoReferrer = 1 << 0, kNoOpener = 1 << 1, kOpener = 1 << 2, }; explicit HTMLFormElement(Document&); ~HTMLFormElement() override; void Trace(Visitor*) const override; HTMLFormControlsCollection* elements(); void GetNamedElements(const AtomicString&, HeapVector<Member<Element>>&); unsigned length() const; HTMLElement* item(unsigned index); String action() const; void setAction(const AtomicString&); String enctype() const { return attributes_.EncodingType(); } void setEnctype(const AtomicString&); String encoding() const { return attributes_.EncodingType(); } void setEncoding(const AtomicString& value) { setEnctype(value); } DOMTokenList& relList() const; bool HasRel(RelAttribute relation) const; bool ShouldAutocomplete() const; void Associate(ListedElement&); void Disassociate(ListedElement&); void Associate(HTMLImageElement&); void Disassociate(HTMLImageElement&); void DidAssociateByParser(); void PrepareForSubmission(const Event*, HTMLFormControlElement* submit_button); void submitFromJavaScript(); void requestSubmit(ExceptionState& exception_state); void requestSubmit(HTMLElement* submitter, ExceptionState& exception_state); void reset(); void AttachLayoutTree(AttachContext& context) override; void DetachLayoutTree(bool performing_reattach) override; void SubmitImplicitly(const Event&, bool from_implicit_submission_trigger); String GetName() const; bool NoValidate() const; const AtomicString& Action() const; String method() const; void setMethod(const AtomicString&); FormSubmission::SubmitMethod Method() const { return attributes_.Method(); } // Find the 'default button.' // https://html.spec.whatwg.org/C/#default-button HTMLFormControlElement* FindDefaultButton() const; bool checkValidity(); bool reportValidity(); bool MatchesValidityPseudoClasses() const final; bool IsValidElement() final; RadioButtonGroupScope& GetRadioButtonGroupScope() { return radio_button_group_scope_; } const ListedElement::List& ListedElements( bool include_shadow_trees = false) const; const HeapVector<Member<HTMLImageElement>>& ImageElements(); V8UnionElementOrRadioNodeList* AnonymousNamedGetter(const AtomicString& name); void InvalidateDefaultButtonStyle() const; // 'construct the entry list' // https://html.spec.whatwg.org/C/#constructing-the-form-data-set // Returns nullptr if this form is already running this function. FormData* ConstructEntryList(HTMLFormControlElement* submit_button, const WTF::TextEncoding& encoding); uint64_t UniqueRendererFormId() const { return unique_renderer_form_id_; } void InvalidateListedElementsIncludingShadowTrees(); private: InsertionNotificationRequest InsertedInto(ContainerNode&) override; void RemovedFrom(ContainerNode&) override; void FinishParsingChildren() override; void HandleLocalEvents(Event&) override; void ParseAttribute(const AttributeModificationParams&) override; bool IsURLAttribute(const Attribute&) const override; bool HasLegalLinkAttribute(const QualifiedName&) const override; NamedItemType GetNamedItemType() const override { return NamedItemType::kName; } void SubmitDialog(FormSubmission*); void ScheduleFormSubmission(const Event*, HTMLFormControlElement* submit_button); void CollectListedElements( const Node& root, ListedElement::List& elements, ListedElement::List* elements_including_shadow_trees = nullptr, bool in_shadow_tree = false) const; void CollectImageElements(Node& root, HeapVector<Member<HTMLImageElement>>&); // Returns true if the submission should proceed. bool ValidateInteractively(); // Validates each of the controls, and stores controls of which 'invalid' // event was not canceled to the specified vector. Returns true if there // are any invalid controls in this form. bool CheckInvalidControlsAndCollectUnhandled(ListedElement::List*); Element* ElementFromPastNamesMap(const AtomicString&); void AddToPastNamesMap(Element*, const AtomicString& past_name); void RemoveFromPastNamesMap(HTMLElement&); typedef HeapHashMap<AtomicString, Member<Element>> PastNamesMap; FormSubmission::Attributes attributes_; Member<PastNamesMap> past_names_map_; RadioButtonGroupScope radio_button_group_scope_; // Do not access listed_elements_ directly. Use ListedElements() instead. ListedElement::List listed_elements_; // Do not access listed_elements_including_shadow_trees_ directly. Use // ListedElements(true) instead. ListedElement::List listed_elements_including_shadow_trees_; // Do not access image_elements_ directly. Use ImageElements() instead. HeapVector<Member<HTMLImageElement>> image_elements_; uint64_t unique_renderer_form_id_; base::OnceClosure cancel_last_submission_; bool is_submitting_ = false; bool in_user_js_submit_event_ = false; bool is_constructing_entry_list_ = false; bool listed_elements_are_dirty_ : 1; bool listed_elements_including_shadow_trees_are_dirty_ : 1; bool image_elements_are_dirty_ : 1; bool has_elements_associated_by_parser_ : 1; bool has_elements_associated_by_form_attribute_ : 1; bool did_finish_parsing_children_ : 1; bool is_in_reset_function_ : 1; Member<RelList> rel_list_; unsigned rel_attribute_ = 0;};} // namespace blink#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_FORM_ELEMENT_H_
2.3)、v8下form接口实现:
out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_html_form_element.h
out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_html_form_element.cc
截取部分定义:
void MethodAttributeGetCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_HTMLFormElement_method_Getter");BLINK_BINDINGS_TRACE_EVENT("HTMLFormElement.method.get");v8::Isolate* isolate = info.GetIsolate();v8::Local<v8::Object> v8_receiver = info.This();HTMLFormElement* blink_receiver = V8HTMLFormElement::ToWrappableUnsafe(isolate, v8_receiver);auto&& return_value = blink_receiver->method();bindings::V8SetReturnValue(info, return_value, isolate, bindings::V8ReturnValue::kNonNullable);}void MethodAttributeSetCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_HTMLFormElement_method_Setter");BLINK_BINDINGS_TRACE_EVENT("HTMLFormElement.method.set");v8::Isolate* isolate = info.GetIsolate();const ExceptionContextType exception_context_type = ExceptionContextType::kAttributeSet;const char* const class_like_name = "HTMLFormElement";const char* const property_name = "method";ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);// [CEReactions]CEReactionsScope ce_reactions_scope;v8::Local<v8::Object> v8_receiver = info.This();HTMLFormElement* blink_receiver = V8HTMLFormElement::ToWrappableUnsafe(isolate, v8_receiver);v8::Local<v8::Value> v8_property_value = info[0];auto&& arg1_value = NativeValueTraits<IDLString>::NativeValue(isolate, v8_property_value, exception_state);if (UNLIKELY(exception_state.HadException())) { return;}blink_receiver->setMethod(arg1_value);}
2.4)、form_datat数据填充接口:
FormData用来构建FormSubmission对象主要存储form表单内的元素参数:
例如:
First name: <input type="text" name="fname"><br>
Last name: <input type="text" name="lname"><br>
会转换成FirstName=Mickey&LastName=Mouse,最后放到url请求参数里面。
结果:
https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse
hird_party\blink\renderer\core\html\forms\form_data.idl
// https://xhr.spec.whatwg.org/#interface-formdatatypedef (File or USVString) FormDataEntryValue;[ Exposed=(Window,Worker)] interface FormData { [RaisesException] constructor(optional HTMLFormElement form, optional HTMLElement? submitter = null); void append(USVString name, USVString value); [CallWith=ScriptState] void append(USVString name, Blob value, optional USVString filename); [ImplementedAs=deleteEntry] void delete(USVString name); FormDataEntryValue? get(USVString name); sequence<FormDataEntryValue> getAll(USVString name); boolean has(USVString name); void set(USVString name, USVString value); void set(USVString name, Blob value, optional USVString filename); iterable<USVString, FormDataEntryValue>;};
blink和v8定义:
third_party\blink\renderer\core\html\forms\form_data.h
third_party\blink\renderer\core\html\forms\form_data.cc
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_#include "third_party/blink/renderer/bindings/core/v8/iterable.h"#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_form_data.h"#include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"#include "third_party/blink/renderer/core/core_export.h"#include "third_party/blink/renderer/core/html/html_element.h"#include "third_party/blink/renderer/platform/heap/garbage_collected.h"#include "third_party/blink/renderer/platform/network/encoded_form_data.h"#include "third_party/blink/renderer/platform/wtf/forward.h"#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"namespace blink {class Blob;class File;class FormControlState;class HTMLFormElement;class ScriptState;class ExecutionContext;class CORE_EXPORT FormData final : public ScriptWrappable, public PairSyncIterable<FormData> { DEFINE_WRAPPERTYPEINFO(); public: static FormData* Create(ExceptionState& exception_state) { return MakeGarbageCollected<FormData>(); } static FormData* Create(HTMLFormElement* form, ExceptionState& exception_state); static FormData* Create(HTMLFormElement* form, HTMLElement* submitter, ExceptionState& exception_state); explicit FormData(const WTF::TextEncoding&); // Clones form_data. This clones |form_data.entries_| Vector, but // doesn't clone entries in it because they are immutable. FormData(const FormData& form_data); FormData(); void Trace(Visitor*) const override; // FormData IDL interface. void append(const String& name, const String& value); void append(ScriptState*, const String& name, Blob*, const String& filename = String()); void deleteEntry(const String& name); V8FormDataEntryValue* get(const String& name); HeapVector<Member<V8FormDataEntryValue>> getAll(const String& name); bool has(const String& name); void set(const String& name, const String& value); void set(const String& name, Blob*, const String& filename = String()); // Internal functions. const WTF::TextEncoding& Encoding() const { return encoding_; } std::string Encode(const String& key) const; class Entry; const HeapVector<Member<const Entry>>& Entries() const { return entries_; } size_t size() const { return entries_.size(); } void append(const String& name, Blob*, const String& filename = String()); void AppendFromElement(const String& name, int value); void AppendFromElement(const String& name, File* file); void AppendFromElement(const String& name, const String& value); // This flag is true if this FormData is created with a <form>, and its // associated elements contain a non-empty password field. bool ContainsPasswordData() const { return contains_password_data_; } void SetContainsPasswordData(bool flag) { contains_password_data_ = flag; } scoped_refptr<EncodedFormData> EncodeFormData( EncodedFormData::EncodingType = EncodedFormData::kFormURLEncoded); scoped_refptr<EncodedFormData> EncodeMultiPartFormData(); void AppendToControlState(FormControlState& state) const; static FormData* CreateFromControlState(ExecutionContext& execution_context, const FormControlState& state, wtf_size_t& index); private: void SetEntry(const Entry*); IterationSource* CreateIterationSource(ScriptState*, ExceptionState&) override; WTF::TextEncoding encoding_; // Entry pointers in entries_ never be nullptr. HeapVector<Member<const Entry>> entries_; bool contains_password_data_ = false;};// Represents entry, which is a pair of a name and a value.// https://xhr.spec.whatwg.org/#concept-formdata-entry// Entry objects are immutable.class FormData::Entry final : public GarbageCollected<FormData::Entry> { public: Entry(const String& name, const String& value); Entry(const String& name, Blob* blob, const String& filename); void Trace(Visitor*) const; bool IsString() const { return !blob_; } bool isFile() const { return blob_ != nullptr; } const String& name() const { return name_; } const String& Value() const { return value_; } Blob* GetBlob() const { return blob_.Get(); } CORE_EXPORT File* GetFile() const; const String& Filename() const { return filename_; } private: const String name_; const String value_; const Member<Blob> blob_; const String filename_;};} // namespace blink#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_
out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_form_data.h
out\Debug\gen\third_party\blink\renderer\bindings\core\v8\v8_form_data.cc
截图部分实现:
void KeysOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_FormData_keys");BLINK_BINDINGS_TRACE_EVENT("FormData.keys");v8::Isolate* isolate = info.GetIsolate();v8::Local<v8::Object> v8_receiver = info.This();FormData* blink_receiver = V8FormData::ToWrappableUnsafe(isolate, v8_receiver);v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();ScriptState* receiver_script_state = ScriptState::From(receiver_context);ScriptState* script_state = receiver_script_state;const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;const char* const class_like_name = "FormData";const char* const property_name = "keys";ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);auto&& return_value = blink_receiver->keysForBinding(script_state, exception_state);if (UNLIKELY(exception_state.HadException())) { return;}bindings::V8SetReturnValue(info, return_value, blink_receiver);}void ValuesOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_FormData_values");BLINK_BINDINGS_TRACE_EVENT("FormData.values");v8::Isolate* isolate = info.GetIsolate();v8::Local<v8::Object> v8_receiver = info.This();FormData* blink_receiver = V8FormData::ToWrappableUnsafe(isolate, v8_receiver);v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();ScriptState* receiver_script_state = ScriptState::From(receiver_context);ScriptState* script_state = receiver_script_state;const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;const char* const class_like_name = "FormData";const char* const property_name = "values";ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);auto&& return_value = blink_receiver->valuesForBinding(script_state, exception_state);if (UNLIKELY(exception_state.HadException())) { return;}bindings::V8SetReturnValue(info, return_value, blink_receiver);}
2.5)、form submit 管理类FormSubmission
网页点击submit会调用到FormSubmission。
third_party\blink\renderer\core\loader\form_submission.h
third_party\blink\renderer\core\loader\form_submission.cc
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_#include "third_party/blink/public/common/tokens/tokens.h"#include "third_party/blink/public/mojom/frame/policy_container.mojom-blink.h"#include "third_party/blink/public/mojom/frame/triggering_event_info.mojom-blink-forward.h"#include "third_party/blink/public/web/web_frame_load_type.h"#include "third_party/blink/renderer/core/loader/frame_loader_types.h"#include "third_party/blink/renderer/core/loader/navigation_policy.h"#include "third_party/blink/renderer/platform/heap/garbage_collected.h"#include "third_party/blink/renderer/platform/weborigin/kurl.h"#include "third_party/blink/renderer/platform/heap/member.h"#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"namespace blink {class Element;class EncodedFormData;class Event;class Frame;class HTMLFormControlElement;class HTMLFormElement;class LocalDOMWindow;class ResourceRequest;class SourceLocation;class FormSubmission final : public GarbageCollected<FormSubmission> { public: enum SubmitMethod { kGetMethod, kPostMethod, kDialogMethod }; class Attributes { DISALLOW_NEW(); public: Attributes() : method_(kGetMethod), is_multi_part_form_(false), encoding_type_("application/x-www-form-urlencoded") {} Attributes(const Attributes&) = delete; Attributes& operator=(const Attributes&) = delete; SubmitMethod Method() const { return method_; } static SubmitMethod ParseMethodType(const String&); void UpdateMethodType(const String&); static String MethodString(SubmitMethod); const String& Action() const { return action_; } void ParseAction(const String&); const AtomicString& Target() const { return target_; } void SetTarget(const AtomicString& target) { target_ = target; } const AtomicString& EncodingType() const { return encoding_type_; } static AtomicString ParseEncodingType(const String&); void UpdateEncodingType(const String&); bool IsMultiPartForm() const { return is_multi_part_form_; } const String& AcceptCharset() const { return accept_charset_; } void SetAcceptCharset(const String& value) { accept_charset_ = value; } void CopyFrom(const Attributes&); private: SubmitMethod method_; bool is_multi_part_form_; String action_; AtomicString target_; AtomicString encoding_type_; String accept_charset_; }; // Create FormSubmission // // This returns nullptr if form submission is not allowed for the given // arguments. For example, if navigation policy for the event is // `kNavigationPolicyLinkPreview`. static FormSubmission* Create(HTMLFormElement*, const Attributes&, const Event*, HTMLFormControlElement* submit_button); FormSubmission( SubmitMethod, const KURL& action, const AtomicString& target, const AtomicString& content_type, Element* submitter, scoped_refptr<EncodedFormData>, const Event*, NavigationPolicy navigation_policy, mojom::blink::TriggeringEventInfo triggering_event_info, ClientNavigationReason reason, std::unique_ptr<ResourceRequest> resource_request, Frame* target_frame, WebFrameLoadType load_type, LocalDOMWindow* origin_window, const LocalFrameToken& initiator_frame_token, std::unique_ptr<SourceLocation> source_location, mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle> initiator_policy_container_keep_alive_handle); // FormSubmission for DialogMethod explicit FormSubmission(const String& result); void Trace(Visitor*) const; void Navigate(); KURL RequestURL() const; SubmitMethod Method() const { return method_; } const KURL& Action() const { return action_; } EncodedFormData* Data() const { return form_data_.get(); } const String& Result() const { return result_; } Frame* TargetFrame() const { return target_frame_.Get(); } private: // FIXME: Hold an instance of Attributes instead of individual members. SubmitMethod method_; KURL action_; AtomicString target_; AtomicString content_type_; Member<Element> submitter_; scoped_refptr<EncodedFormData> form_data_; NavigationPolicy navigation_policy_; mojom::blink::TriggeringEventInfo triggering_event_info_; String result_; ClientNavigationReason reason_; std::unique_ptr<ResourceRequest> resource_request_; Member<Frame> target_frame_; WebFrameLoadType load_type_; Member<LocalDOMWindow> origin_window_; LocalFrameToken initiator_frame_token_; // Since form submissions are scheduled asynchronously, we need to store the // source location when we create the form submission and then pass it over to // the `FrameLoadRequest`. Capturing the source location later when creating // the `FrameLoadRequest` will not return the correct location. std::unique_ptr<SourceLocation> source_location_; // Since form submissions are scheduled asynchronously, we need to keep a // handle to the initiator PolicyContainerHost. This ensures that it remains // available in the browser until we create the NavigationRequest. mojo::PendingRemote<mojom::blink::PolicyContainerHostKeepAliveHandle> initiator_policy_container_keep_alive_handle_;};} // namespace blink#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FORM_SUBMISSION_H_
2.6)、submit_input类对应前端<input type="submit":
third_party\blink\renderer\core\html\forms\submit_input_type.h
third_party\blink\renderer\core\html\forms\submit_input_type.cc
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_INPUT_TYPE_H_#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_INPUT_TYPE_H_#include "third_party/blink/renderer/core/html/forms/base_button_input_type.h"namespace blink {class SubmitInputType final : public BaseButtonInputType { public: explicit SubmitInputType(HTMLInputElement& element); private: void AppendToFormData(FormData&) const override; bool SupportsRequired() const override; void HandleDOMActivateEvent(Event&) override; bool CanBeSuccessfulSubmitButton() override; String DefaultLabel() const override; bool IsTextButton() const override; void ValueAttributeChanged() override;};template <>struct DowncastTraits<SubmitInputType> { static bool AllowFrom(const InputType& type) { return type.IsSubmitInputType(); }};} // namespace blink#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_INPUT_TYPE_H_
三、看下form表单提交过程:
form表单提交过程:
1、render进程点击提交<input type="submit" value="提交">
2、render进程在HTMLFormElement类中构建 FormSubmission并解析<input>内容、从而构建出请求参数:FirstName=Mickey&LastName=Mouse 以及
获取method="get"
/*
First name: <input type="text" name="FirstName" value="Mickey"><br>
Last name: <input type="text" name="LastName" value="Mouse"><br>
*/
最后拼接出请求URL
https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse
3、给主进程发送message.set_method_name("CreateNewWindow");
在新标签中打开https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse。
============================优雅的分割线===================================
1、render进程点击提交<input type="submit" value="提交">
SubmitInputType::HandleDOMActivateEvent(Event& event)
void SubmitInputType::HandleDOMActivateEvent(Event& event) { if (GetElement().IsDisabledFormControl() || !GetElement().Form()) return; // Event handlers can run. GetElement().Form()->PrepareForSubmission(&event, &GetElement()); event.SetDefaultHandled();}
2、HTMLFormElement::ScheduleFormSubmission 函数
调用 FormSubmission* form_submission =
FormSubmission::Create(this, attributes_, event, submit_button);
void HTMLFormElement::ScheduleFormSubmission( const Event* event, HTMLFormControlElement* submit_button) { LocalFrameView* view = GetDocument().View(); LocalFrame* frame = GetDocument().GetFrame(); if (!view || !frame || !frame->GetPage()) return; // https://html.spec.whatwg.org/C/#form-submission-algorithm // 2. If form document is not connected, has no associated browsing context, // or its active sandboxing flag set has its sandboxed forms browsing // context flag set, then abort these steps without doing anything. if (!isConnected()) { GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( mojom::ConsoleMessageSource::kJavaScript, mojom::ConsoleMessageLevel::kWarning, "Form submission canceled because the form is not connected")); return; } if (is_constructing_entry_list_) { GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( mojom::ConsoleMessageSource::kJavaScript, mojom::ConsoleMessageLevel::kWarning, "Form submission canceled because the form is " "constructing entry list")); return; } if (is_submitting_) return; // Delay dispatching 'close' to dialog until done submitting. EventQueueScope scope_for_dialog_close; base::AutoReset<bool> submit_scope(&is_submitting_, true); if (event && !submit_button) { // In a case of implicit submission without a submit button, 'submit' // event handler might add a submit button. We search for a submit // button again. // TODO(tkent): Do we really need to activate such submit button? for (ListedElement* listed_element : ListedElements()) { auto* control = DynamicTo<HTMLFormControlElement>(listed_element); if (!control) continue; DCHECK(!control->IsActivatedSubmit()); if (control->IsSuccessfulSubmitButton()) { submit_button = control; break; } } } FormSubmission* form_submission = FormSubmission::Create(this, attributes_, event, submit_button); if (!form_submission) { // Form submission is not allowed for some NavigationPolicies, e.g. Link // Preview. If an user triggered such user event for form submission, just // ignores it. return; } Frame* target_frame = form_submission->TargetFrame(); // 'formdata' event handlers might disconnect the form. if (!isConnected()) { GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( mojom::ConsoleMessageSource::kJavaScript, mojom::ConsoleMessageLevel::kWarning, "Form submission canceled because the form is not connected")); return; } if (form_submission->Method() == FormSubmission::kDialogMethod) { SubmitDialog(form_submission); return; } DCHECK(form_submission->Method() == FormSubmission::kPostMethod || form_submission->Method() == FormSubmission::kGetMethod); DCHECK(form_submission->Data()); if (form_submission->Action().IsEmpty()) return; if (GetExecutionContext()->IsSandboxed( network::mojom::blink::WebSandboxFlags::kForms)) { // FIXME: This message should be moved off the console once a solution to // https://bugs.webkit.org/show_bug.cgi?id=103274 exists. GetExecutionContext()->AddConsoleMessage( MakeGarbageCollected<ConsoleMessage>( mojom::blink::ConsoleMessageSource::kSecurity, mojom::blink::ConsoleMessageLevel::kError, "Blocked form submission to '" + form_submission->Action().ElidedString() + "' because the form's frame is sandboxed and the 'allow-forms' " "permission is not set.")); return; } if (form_submission->Action().ProtocolIsJavaScript()) { // For javascript URLs we need to do the CSP check for 'form-action' here. // All other schemes are checked in the browser. // // TODO(antoniosartori): Should we keep the 'form-action' check for // javascript: URLs? For 'frame-src' and 'navigate-to', we do not check // javascript: URLs. Reading the specification, it looks like 'form-action' // should not apply to javascript: URLs. if (!GetExecutionContext()->GetContentSecurityPolicy()->AllowFormAction( form_submission->Action())) { return; } } UseCounter::Count(GetDocument(), WebFeature::kFormsSubmitted); if (MixedContentChecker::IsMixedFormAction(GetDocument().GetFrame(), form_submission->Action())) { UseCounter::Count(GetDocument(), WebFeature::kMixedContentFormsSubmitted); } if (FastHasAttribute(html_names::kDisabledAttr)) { UseCounter::Count(GetDocument(), WebFeature::kFormDisabledAttributePresentAndSubmit); } if (!target_frame) return; if (form_submission->Action().ProtocolIsJavaScript()) { // For javascript urls, don't post a task to execute the form submission // because we already get another task posted for it in // Document::ProcessJavascriptUrl. If we post two tasks, the javascript will // be run too late according to some tests. form_submission->Navigate(); return; } FrameScheduler* scheduler = GetDocument().GetFrame()->GetFrameScheduler(); if (auto* target_local_frame = DynamicTo<LocalFrame>(target_frame)) { if (!target_local_frame->IsNavigationAllowed()) return; // Cancel parsing if the form submission is targeted at this frame. if (target_local_frame == GetDocument().GetFrame() && !form_submission->Action().ProtocolIsJavaScript()) { target_local_frame->GetDocument()->CancelParsing(); } // Use the target frame's frame scheduler. If we can't due to targeting a // RemoteFrame, then use the frame scheduler from the frame this form is in. scheduler = target_local_frame->GetFrameScheduler(); // Cancel pending javascript url navigations for the target frame. This new // form submission should take precedence over them. target_local_frame->GetDocument()->CancelPendingJavaScriptUrls(); // Cancel any pre-existing attempt to navigate the target frame which was // already sent to the browser process so this form submission will take // precedence over it. target_local_frame->Loader().CancelClientNavigation(); } cancel_last_submission_ = target_frame->ScheduleFormSubmission(scheduler, form_submission);}
3、FormSubmission::Create()函数构建FirstName=Mickey&LastName=Mouse,以及打开提交页面方式:
FormSubmission* FormSubmission::Create(HTMLFormElement* form, const Attributes& attributes, const Event* event, HTMLFormControlElement* submit_button) { DCHECK(form); FormSubmission::Attributes copied_attributes; copied_attributes.CopyFrom(attributes); if (submit_button) { AtomicString attribute_value; if (!(attribute_value = submit_button->FastGetAttribute(html_names::kFormactionAttr)) .IsNull()) copied_attributes.ParseAction(attribute_value); if (!(attribute_value = submit_button->FastGetAttribute(html_names::kFormenctypeAttr)) .IsNull()) copied_attributes.UpdateEncodingType(attribute_value); if (!(attribute_value = submit_button->FastGetAttribute(html_names::kFormmethodAttr)) .IsNull()) copied_attributes.UpdateMethodType(attribute_value); if (!(attribute_value = submit_button->FastGetAttribute(html_names::kFormtargetAttr)) .IsNull()) copied_attributes.SetTarget(attribute_value); } if (copied_attributes.Method() == kDialogMethod) { if (submit_button) { return MakeGarbageCollected<FormSubmission>( submit_button->ResultForDialogSubmit()); } return MakeGarbageCollected<FormSubmission>(""); } Document& document = form->GetDocument(); KURL action_url = document.CompleteURL(copied_attributes.Action().empty() ? document.Url().GetString() : copied_attributes.Action()); if ((document.domWindow()->GetSecurityContext().GetInsecureRequestPolicy() & mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) != mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone && action_url.ProtocolIs("http") && !network::IsUrlPotentiallyTrustworthy(GURL(action_url))) { UseCounter::Count(document, WebFeature::kUpgradeInsecureRequestsUpgradedRequestForm); action_url.SetProtocol("https"); if (action_url.Port() == 80) action_url.SetPort(443); } bool is_mailto_form = action_url.ProtocolIs("mailto"); bool is_multi_part_form = false; AtomicString encoding_type = copied_attributes.EncodingType(); if (copied_attributes.Method() == kPostMethod) { is_multi_part_form = copied_attributes.IsMultiPartForm(); if (is_multi_part_form && is_mailto_form) { encoding_type = AtomicString("application/x-www-form-urlencoded"); is_multi_part_form = false; } } WTF::TextEncoding data_encoding = is_mailto_form ? UTF8Encoding() : FormDataEncoder::EncodingFromAcceptCharset( copied_attributes.AcceptCharset(), document.Encoding()); //解析<input>构建请求参数FirstName=Mickey&LastName=Mouse FormData* dom_form_data = form->ConstructEntryList( submit_button, data_encoding.EncodingForFormSubmission()); DCHECK(dom_form_data); scoped_refptr<EncodedFormData> form_data; String boundary; if (is_multi_part_form) { form_data = dom_form_data->EncodeMultiPartFormData(); boundary = form_data->Boundary().data(); } else { form_data = dom_form_data->EncodeFormData( attributes.Method() == kGetMethod ? EncodedFormData::kFormURLEncoded : EncodedFormData::ParseEncodingType(encoding_type)); if (copied_attributes.Method() == kPostMethod && is_mailto_form) { // Convert the form data into a string that we put into the URL. AppendMailtoPostFormDataToURL(action_url, *form_data, encoding_type); form_data = EncodedFormData::Create(); } } form_data->SetIdentifier(GenerateFormDataIdentifier()); form_data->SetContainsPasswordData(dom_form_data->ContainsPasswordData()); if (copied_attributes.Method() != FormSubmission::kPostMethod && !action_url.ProtocolIsJavaScript()) { action_url.SetQuery(form_data->FlattenToString()); } std::unique_ptr<ResourceRequest> resource_request = std::make_unique<ResourceRequest>(action_url); ClientNavigationReason reason = ClientNavigationReason::kFormSubmissionGet; if (copied_attributes.Method() == FormSubmission::kPostMethod) { reason = ClientNavigationReason::kFormSubmissionPost; resource_request->SetHttpMethod(http_names::kPOST); resource_request->SetHttpBody(form_data); // construct some user headers if necessary if (boundary.empty()) { resource_request->SetHTTPContentType(encoding_type); } else { resource_request->SetHTTPContentType(encoding_type + "; boundary=" + boundary); } } resource_request->SetHasUserGesture( LocalFrame::HasTransientUserActivation(form->GetDocument().GetFrame())); resource_request->SetFormSubmission(true); mojom::blink::TriggeringEventInfo triggering_event_info; if (event) { triggering_event_info = event->isTrusted() ? mojom::blink::TriggeringEventInfo::kFromTrustedEvent : mojom::blink::TriggeringEventInfo::kFromUntrustedEvent; if (event->UnderlyingEvent()) event = event->UnderlyingEvent(); } else { triggering_event_info = mojom::blink::TriggeringEventInfo::kNotFromEvent; } FrameLoadRequest frame_request(form->GetDocument().domWindow(), *resource_request); //设置submit打开策略 NavigationPolicy navigation_policy = NavigationPolicyFromEvent(event); if (navigation_policy == kNavigationPolicyLinkPreview) { return nullptr; } frame_request.SetNavigationPolicy(navigation_policy); frame_request.SetClientRedirectReason(reason); if (submit_button) { frame_request.SetSourceElement(submit_button); } else { frame_request.SetSourceElement(form); } frame_request.SetTriggeringEventInfo(triggering_event_info); AtomicString target_or_base_target = frame_request.CleanNavigationTarget( copied_attributes.Target().empty() ? document.BaseTarget() : copied_attributes.Target()); if (form->HasRel(HTMLFormElement::kNoReferrer)) { frame_request.SetNoReferrer(); frame_request.SetNoOpener(); } if (form->HasRel(HTMLFormElement::kNoOpener) || (EqualIgnoringASCIICase(target_or_base_target, "_blank") && !form->HasRel(HTMLFormElement::kOpener) && form->GetDocument() .domWindow() ->GetFrame() ->GetSettings() ->GetTargetBlankImpliesNoOpenerEnabledWillBeRemoved())) { frame_request.SetNoOpener(); } //给主进程发送message.set_method_name("CreateNewWindow"); Frame* target_frame = form->GetDocument() .GetFrame() ->Tree() .FindOrCreateFrameForNavigation(frame_request, target_or_base_target) .frame; // Apply replacement now, before any async steps, as the result may change. WebFrameLoadType load_type = WebFrameLoadType::kStandard; LocalFrame* target_local_frame = DynamicTo<LocalFrame>(target_frame); if (target_local_frame && target_local_frame->NavigationShouldReplaceCurrentHistoryEntry( frame_request, load_type)) { load_type = WebFrameLoadType::kReplaceCurrentItem; } return MakeGarbageCollected<FormSubmission>( copied_attributes.Method(), action_url, target_or_base_target, encoding_type, frame_request.GetSourceElement(), std::move(form_data), event, frame_request.GetNavigationPolicy(), triggering_event_info, reason, std::move(resource_request), target_frame, load_type, form->GetDocument().domWindow(), form->GetDocument().GetFrame()->GetLocalFrameToken(), CaptureSourceLocation(form->GetDocument().domWindow()), form->GetDocument() .domWindow() ->GetPolicyContainer() ->IssueKeepAliveHandle());}
3.1)、构建FirstName=Mickey&LastName=Mouse 参数
/ /ConstructEntryList函数遍历form表单元素
FormData* dom_form_data = form->ConstructEntryList(
submit_button, data_encoding.EncodingForFormSubmission());
DCHECK(dom_form_data);
3.2)、FormSubmission::Create调用FindOrCreateFrameForNavigation
用来打开提交新页面。
Frame* target_frame =
form->GetDocument()
.GetFrame()
->Tree()
.FindOrCreateFrameForNavigation(frame_request, target_or_base_target)
.frame;
3.3)、FindOrCreateFrameForNavigation调用RenderFrameImpl::CreateNewWindow 函数
RenderFrameImpl::CreateNewWindow函数定义(content\renderer\render_frame_impl.cc):
WebView* RenderFrameImpl::CreateNewWindow( const WebURLRequest& request, const blink::WebWindowFeatures& features, const WebString& frame_name, WebNavigationPolicy policy, network::mojom::WebSandboxFlags sandbox_flags, const blink::SessionStorageNamespaceId& session_storage_namespace_id, bool& consumed_user_gesture, const absl::optional<blink::Impression>& impression, const absl::optional<blink::WebPictureInPictureWindowOptions>& pip_options, const blink::WebURL& base_url) { consumed_user_gesture = false; mojom::CreateNewWindowParamsPtr params = mojom::CreateNewWindowParams::New(); // The user activation check is done at the browser process through // |frame_host->CreateNewWindow()| call below. But the extensions case // handled through the following |if| is an exception. params->allow_popup = false; if (GetContentClient()->renderer()->AllowPopup()) params->allow_popup = true; params->window_container_type = WindowFeaturesToContainerType(features); params->session_storage_namespace_id = session_storage_namespace_id; if (!features.noopener) { params->clone_from_session_storage_namespace_id = GetWebView()->GetSessionStorageNamespaceId(); } const std::string& frame_name_utf8 = frame_name.Utf8( WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD); params->frame_name = frame_name_utf8; params->opener_suppressed = features.noopener; params->disposition = NavigationPolicyToDisposition(policy); if (!request.IsNull()) { params->target_url = request.Url(); // The browser process does not consider empty URLs as valid (partly due to // a risk of treating them as a navigation to the privileged NTP in some // cases), so treat an attempt to create a window with an empty URL as // opening about:blank. // // Similarly, javascript: URLs should not be sent to the browser process, // since they are either handled within the renderer process (if a window is // created within the same browsing context group) or ignored (in the // noopener case). Use about:blank for the URL in that case as well, to // reduce the risk of running them incorrectly. if (params->target_url.is_empty() || params->target_url.SchemeIs(url::kJavaScriptScheme)) { params->target_url = GURL(url::kAboutBlankURL); } params->referrer = blink::mojom::Referrer::New( blink::WebStringToGURL(request.ReferrerString()), request.GetReferrerPolicy()); } params->features = ConvertWebWindowFeaturesToMojoWindowFeatures(features); params->is_form_submission = request.IsFormSubmission(); params->form_submission_post_data = blink::GetRequestBodyForWebURLRequest(request); params->form_submission_post_content_type = request.HttpContentType().Utf8(); params->impression = impression; if (pip_options) { CHECK_EQ(policy, blink::kWebNavigationPolicyPictureInPicture); auto pip_mojom_opts = blink::mojom::PictureInPictureWindowOptions::New(); pip_mojom_opts->width = pip_options->width; pip_mojom_opts->height = pip_options->height; // TODO(crbug.com/1444658): Remove this from mojom and the browser side. pip_mojom_opts->initial_aspect_ratio = 0.0; // TODO(crbug.com/1410379): Remove this from mojom and the browser side. pip_mojom_opts->lock_aspect_ratio = false; params->pip_options = std::move(pip_mojom_opts); } params->download_policy.ApplyDownloadFramePolicy( /*is_opener_navigation=*/false, request.HasUserGesture(), // `openee_can_access_opener_origin` only matters for opener navigations, // so its value here is irrelevant. /*openee_can_access_opener_origin=*/true, !GetWebFrame()->IsAllowedToDownload(), GetWebFrame()->IsAdFrame()); params->initiator_activation_and_ad_status = blink::GetNavigationInitiatorActivationAndAdStatus( request.HasUserGesture(), GetWebFrame()->IsAdFrame(), GetWebFrame()->IsAdScriptInStack()); // We preserve this information before sending the message since |params| is // moved on send. bool is_background_tab = params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB; mojom::CreateNewWindowStatus status; mojom::CreateNewWindowReplyPtr reply; auto* frame_host = GetFrameHost(); if (!frame_host->CreateNewWindow(std::move(params), &status, &reply)) { // The sync IPC failed, e.g. maybe the render process is in the middle of // shutting down. Can't create a new window without the browser process, // so just bail out. return nullptr; } // If creation of the window was blocked (e.g. because this frame doesn't // have user activation), return before consuming user activation. A frame // that isn't allowed to open a window shouldn't be able to consume the // activation for the rest of the frame tree. if (status == mojom::CreateNewWindowStatus::kBlocked) return nullptr; // For Android WebView, we support a pop-up like behavior for window.open() // even if the embedding app doesn't support multiple windows. In this case, // window.open() will return "window" and navigate it to whatever URL was // passed. We also don't need to consume user gestures to protect against // multiple windows being opened, because, well, the app doesn't support // multiple windows. // TODO(dcheng): It's awkward that this is plumbed into Blink but not really // used much in Blink, except to enable web testing... perhaps this should // be checked directly in the browser side. if (status == mojom::CreateNewWindowStatus::kReuse) { // In this case, treat javascript: URLs as blocked rather than running them // in a reused main frame in Android WebView. See https://crbug.com/1083819. if (!request.IsNull() && request.Url().ProtocolIs(url::kJavaScriptScheme)) { return nullptr; } return GetWebView(); } // Consume the transient user activation in the current renderer. consumed_user_gesture = GetWebFrame()->ConsumeTransientUserActivation( blink::UserActivationUpdateSource::kBrowser); // If we should ignore the new window (e.g. because of `noopener`), return // now that user activation was consumed. if (status == mojom::CreateNewWindowStatus::kIgnore) return nullptr; DCHECK(reply); DCHECK_NE(MSG_ROUTING_NONE, reply->main_frame_route_id); DCHECK_NE(MSG_ROUTING_NONE, reply->widget_params->routing_id); // While this view may be a background extension page, it can spawn a visible // render view. So we just assume that the new one is not another background // page instead of passing on our own value. // TODO(vangelis): Can we tell if the new view will be a background page? bool never_composited = false; // The initial hidden state for the RenderViewImpl here has to match what the // browser will eventually decide for the given disposition. Since we have to // return from this call synchronously, we just have to make our best guess // and rely on the browser sending a WasHidden / WasShown message if it // disagrees. mojom::CreateViewParamsPtr view_params = mojom::CreateViewParams::New(); view_params->opener_frame_token = GetWebFrame()->GetFrameToken(); view_params->window_was_opened_by_another_window = true; view_params->renderer_preferences = GetWebView()->GetRendererPreferences(); view_params->web_preferences = GetWebView()->GetWebPreferences(); view_params->replication_state = blink::mojom::FrameReplicationState::New(); view_params->replication_state->frame_policy.sandbox_flags = sandbox_flags; view_params->replication_state->name = frame_name_utf8; view_params->devtools_main_frame_token = reply->devtools_main_frame_token; view_params->browsing_context_group_info = reply->browsing_context_group_info; view_params->color_provider_colors = reply->color_provider_colors; auto main_frame_params = mojom::CreateLocalMainFrameParams::New(); main_frame_params->frame_token = reply->main_frame_token; main_frame_params->routing_id = reply->main_frame_route_id; main_frame_params->frame = std::move(reply->frame); main_frame_params->interface_broker = std::move(reply->main_frame_interface_broker); main_frame_params->document_token = reply->document_token; main_frame_params->policy_container = std::move(reply->policy_container); main_frame_params->associated_interface_provider_remote = std::move(reply->associated_interface_provider); main_frame_params->widget_params = std::move(reply->widget_params); main_frame_params->subresource_loader_factories = base::WrapUnique(static_cast<blink::PendingURLLoaderFactoryBundle*>( CloneLoaderFactories()->Clone().release())); view_params->main_frame = mojom::CreateMainFrameUnion::NewLocalParams(std::move(main_frame_params)); view_params->blink_page_broadcast = std::move(reply->page_broadcast); view_params->session_storage_namespace_id = reply->cloned_session_storage_namespace_id; DCHECK(!view_params->session_storage_namespace_id.empty()) << "Session storage namespace must be populated."; view_params->hidden = is_background_tab; view_params->never_composited = never_composited; WebView* web_view = agent_scheduling_group_->CreateWebView( std::move(view_params), /*was_created_by_renderer=*/true, base_url); if (reply->wait_for_debugger) { blink::WebFrameWidget* frame_widget = web_view->MainFrame()->ToWebLocalFrame()->LocalRoot()->FrameWidget(); frame_widget->WaitForDebuggerWhenShown(); } return web_view;}
堆栈图:
@3content.dll!content::RenderFrameImpl::CreateNewWindow(const blink::WebURLRequest & request, const blink::WebWindowFeatures & features, const blink::WebString & frame_name, blink::WebNavigationPolicy policy, network::mojom::WebSandboxFlags sandbox_flags, const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & session_storage_namespace_id, bool & consumed_user_gesture, const std::__Cr::optional<blink::Impression> & impression, const std::__Cr::optional<blink::WebPictureInPictureWindowOptions> & pip_options, const blink::WebURL & base_url) 行 6562C++已加载符号。@2 blink_core.dll!blink::ChromeClientImpl::CreateWindowDelegate(blink::LocalFrame * frame, const blink::FrameLoadRequest & r, const WTF::AtomicString & name, const blink::WebWindowFeatures & features, network::mojom::WebSandboxFlags sandbox_flags, const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & session_storage_namespace_id, bool & consumed_user_gesture) 行 342C++已加载符号。 @1blink_core.dll!blink::ChromeClient::CreateWindow(blink::LocalFrame * frame, const blink::FrameLoadRequest & r, const WTF::AtomicString & frame_name, const blink::WebWindowFeatures & features, network::mojom::WebSandboxFlags sandbox_flags, const std::__Cr::basic_string<char,std::__Cr::char_traits<char>,std::__Cr::allocator<char>> & session_storage_namespace_id, bool & consumed_user_gesture) 行 88C++已加载符号。 @0blink_core.dll!blink::CreateNewWindow(blink::LocalFrame & opener_frame, blink::FrameLoadRequest & request, const WTF::AtomicString & frame_name) 行 355C++已加载符号。>blink_core.dll!blink::FrameTree::FindOrCreateFrameForNavigation(blink::FrameLoadRequest & request, const WTF::AtomicString & name) 行 217C++已加载符号。
3.4)、RenderFrameImpl::CreateNewWindow 调用frame_host->CreateNewWindow 给主进程发送CreateNewWindow的mojom消息。
mojom::CreateNewWindowStatus status;
mojom::CreateNewWindowReplyPtr reply;
auto* frame_host = GetFrameHost();
//给主进程发送CreateNewWindow mojom消息
if (!frame_host->CreateNewWindow(std::move(params), &status, &reply)) {
// The sync IPC failed, e.g. maybe the render process is in the middle of
// shutting down. Can't create a new window without the browser process,
// so just bail out.
return nullptr;
}
4、主进程收到子进程发送的mojom CreateNewWindow消息:
在RenderFrameHostImpl::CreateNewWindow函数里面处理打开 submit提交URL="https://www.runoob.com/try/demo_source/demo-form.php?FirstName=Mickey&LastName=Mouse"
src\content\browser\renderer_host\render_frame_host_impl.cc
浏览器打开了一个submit提交的新标签,至此form提交完成。
void RenderFrameHostImpl::CreateNewWindow( mojom::CreateNewWindowParamsPtr params, CreateNewWindowCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); TRACE_EVENT2("navigation", "RenderFrameHostImpl::CreateNewWindow", "render_frame_host", this, "url", params->target_url); // Only top-most frames can open picture-in-picture windows. if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE && GetParentOrOuterDocumentOrEmbedder()) { frame_host_associated_receiver_.ReportBadMessage( "Only top-most frames can open picture-in-picture windows."); return; } bool no_javascript_access = false; // Filter out URLs to which navigation is disallowed from this context. GetProcess()->FilterURL(false, ¶ms->target_url); bool effective_transient_activation_state = params->allow_popup || HasTransientUserActivation() || (transient_allow_popup_.IsActive() && params->disposition == WindowOpenDisposition::NEW_POPUP); // Ignore window creation when sent from a frame that's not active or // created. bool can_create_window = IsActive() && is_render_frame_created() && GetContentClient()->browser()->CanCreateWindow( this, GetLastCommittedURL(), GetOutermostMainFrame()->GetLastCommittedURL(), last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To<Referrer>(), params->frame_name, params->disposition, *params->features, effective_transient_activation_state, params->opener_suppressed, &no_javascript_access); // If this frame isn't allowed to create a window, return early (before we // consume transient user activation). if (!can_create_window) { std::move(callback).Run(mojom::CreateNewWindowStatus::kBlocked, nullptr); return; } // Otherwise, consume user activation before we proceed. In particular, it is // important to do this before we return from the |opener_suppressed| case // below. // NB: This call will consume activations in the browser and the remote frame // proxies for this frame. The initiating renderer will consume its view of // the activations after we return. // See `owner_` invariants about `IsActive()`, which is implied by // `can_create_window`. CHECK(owner_); bool was_consumed = owner_->UpdateUserActivationState( blink::mojom::UserActivationUpdateType::kConsumeTransientActivation, blink::mojom::UserActivationNotificationType::kNone); // For Android WebView, we support a pop-up like behavior for window.open() // even if the embedding app doesn't support multiple windows. In this case, // window.open() will return "window" and navigate it to whatever URL was // passed. if (!GetOrCreateWebPreferences().supports_multiple_windows) { std::move(callback).Run(mojom::CreateNewWindowStatus::kReuse, nullptr); return; } // This will clone the sessionStorage for namespace_id_to_clone. StoragePartition* storage_partition = GetStoragePartition(); DOMStorageContextWrapper* dom_storage_context = static_cast<DOMStorageContextWrapper*>( storage_partition->GetDOMStorageContext()); scoped_refptr<SessionStorageNamespaceImpl> cloned_namespace; if (!params->clone_from_session_storage_namespace_id.empty()) { cloned_namespace = SessionStorageNamespaceImpl::CloneFrom( dom_storage_context, params->session_storage_namespace_id, params->clone_from_session_storage_namespace_id); } else { cloned_namespace = SessionStorageNamespaceImpl::Create( dom_storage_context, params->session_storage_namespace_id); } if (IsCredentialless() || IsNestedWithinFencedFrame() || CoopSuppressOpener(/*opener=*/this)) { params->opener_suppressed = true; // TODO(https://crbug.com/1060691) This should be applied to all // popups opened with noopener. params->frame_name.clear(); } RenderFrameHostImpl* top_level_opener = GetMainFrame(); int popup_virtual_browsing_context_group = params->opener_suppressed ? CrossOriginOpenerPolicyAccessReportManager:: GetNewVirtualBrowsingContextGroup() : top_level_opener->virtual_browsing_context_group(); int popup_soap_by_default_virtual_browsing_context_group = params->opener_suppressed ? CrossOriginOpenerPolicyAccessReportManager:: GetNewVirtualBrowsingContextGroup() : top_level_opener->soap_by_default_virtual_browsing_context_group(); // If the opener is suppressed or script access is disallowed, we should // open the window in a new BrowsingInstance, and thus a new process. That // means the current renderer process will not be able to route messages to // it. Because of this, we will immediately show and navigate the window // in OnCreateNewWindowOnUI, using the params provided here. bool is_new_browsing_instance = params->opener_suppressed || no_javascript_access; DCHECK(IsRenderFrameLive()); // The non-owning pointer |new_frame_tree| is valid in this stack frame since // nothing can delete it until this thread is freed up again. FrameTree* new_frame_tree = delegate_->CreateNewWindow(this, *params, is_new_browsing_instance, was_consumed, cloned_namespace.get()); transient_allow_popup_.Deactivate(); MaybeRecordAdClickMainFrameNavigationUseCounter( /*initiator_frame=*/this, params->initiator_activation_and_ad_status); if (is_new_browsing_instance || !new_frame_tree) { // Opener suppressed, Javascript access disabled, or delegate did not // provide a handle to any windows it created. In these cases, never tell // the renderer about the new window. std::move(callback).Run(mojom::CreateNewWindowStatus::kIgnore, nullptr); return; } DCHECK(!params->opener_suppressed); RenderFrameHostImpl* new_main_rfh = new_frame_tree->root()->current_frame_host(); new_main_rfh->virtual_browsing_context_group_ = popup_virtual_browsing_context_group; new_main_rfh->soap_by_default_virtual_browsing_context_group_ = popup_soap_by_default_virtual_browsing_context_group; // COOP and COOP reporter are inherited from the opener to the popup's initial // empty document. if (IsOpenerSameOriginFrame(/*opener=*/this) && GetMainFrame()->coop_access_report_manager()->coop_reporter()) { new_main_rfh->SetCrossOriginOpenerPolicyReporter( std::make_unique<CrossOriginOpenerPolicyReporter>( GetProcess()->GetStoragePartition(), GetLastCommittedURL(), params->referrer->url, // TODO(https://crbug.com/1385827): See if we need to send the // origin to reporters as well. new_main_rfh->cross_origin_opener_policy(), GetReportingSource(), isolation_info_.network_anonymization_key())); } mojo::PendingAssociatedRemote<mojom::Frame> pending_frame_remote; mojo::PendingAssociatedReceiver<mojom::Frame> pending_frame_receiver = pending_frame_remote.InitWithNewEndpointAndPassReceiver(); new_main_rfh->SetMojomFrameRemote(std::move(pending_frame_remote)); mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> browser_interface_broker; new_main_rfh->BindBrowserInterfaceBrokerReceiver( browser_interface_broker.InitWithNewPipeAndPassReceiver()); mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider> pending_associated_interface_provider; new_main_rfh->BindAssociatedInterfaceProviderReceiver( pending_associated_interface_provider .InitWithNewEndpointAndPassReceiver()); // With this path, RenderViewHostImpl::CreateRenderView is never called // because `blink::WebView` is already created on the renderer side. Thus we // need to establish the connection here. mojo::PendingAssociatedRemote<blink::mojom::PageBroadcast> page_broadcast; mojo::PendingAssociatedReceiver<blink::mojom::PageBroadcast> page_broadcast_receiver = page_broadcast.InitWithNewEndpointAndPassReceiver(); auto widget_params = new_main_rfh->GetLocalRenderWidgetHost() ->BindAndGenerateCreateFrameWidgetParamsForNewWindow(); new_main_rfh->render_view_host()->BindPageBroadcast( std::move(page_broadcast)); bool wait_for_debugger = devtools_instrumentation::ShouldWaitForDebuggerInWindowOpen(); mojom::CreateNewWindowReplyPtr reply = mojom::CreateNewWindowReply::New( new_main_rfh->GetFrameToken(), new_main_rfh->GetRoutingID(), std::move(pending_frame_receiver), std::move(widget_params), std::move(page_broadcast_receiver), std::move(browser_interface_broker), std::move(pending_associated_interface_provider), cloned_namespace->id(), new_main_rfh->GetDevToolsFrameToken(), wait_for_debugger, new_main_rfh->GetDocumentToken(), new_main_rfh->policy_container_host()->CreatePolicyContainerForBlink(), blink::BrowsingContextGroupInfo( new_main_rfh->GetSiteInstance()->browsing_instance_token(), new_main_rfh->GetSiteInstance()->coop_related_group_token()), delegate_->GetColorProviderColorMaps()); std::move(callback).Run(mojom::CreateNewWindowStatus::kSuccess, std::move(reply)); // The mojom reply callback with kSuccess causes the renderer to create the // renderer-side objects. new_main_rfh->render_view_host()->RenderViewCreated(new_main_rfh);}
子进程发送消息定义在
F:\code\google\src\out\Debug\gen\content\common\frame.mojom.cc
message.set_method_name("CreateNewWindow");
FrameHostProxy::FrameHostProxy(mojo::MessageReceiverWithResponder* receiver) : receiver_(receiver) {}bool FrameHostProxy::CreateNewWindow( CreateNewWindowParamsPtr param_params, CreateNewWindowStatus* out_param_status, CreateNewWindowReplyPtr* out_param_reply) {#if BUILDFLAG(MOJO_TRACE_ENABLED) TRACE_EVENT_BEGIN1( "mojom", "Call content::mojom::FrameHost::CreateNewWindow (sync)", "input_parameters", [&](perfetto::TracedValue context){ auto dict = std::move(context).WriteDictionary(); perfetto::WriteIntoTracedValueWithFallback( dict.AddItem("params"), param_params, "<value of type CreateNewWindowParamsPtr>"); });#else TRACE_EVENT0("mojom", "FrameHost::CreateNewWindow");#endif const bool kExpectsResponse = true; const bool kIsSync = true; const bool kAllowInterrupt = true; const bool is_urgent = false; const uint32_t kFlags = ((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) | ((kIsSync) ? mojo::Message::kFlagIsSync : 0) | ((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) | ((is_urgent) ? mojo::Message::kFlagIsUrgent : 0); mojo::Message message( internal::kFrameHost_CreateNewWindow_Name, kFlags, 0, 0, nullptr); mojo::internal::MessageFragment< ::content::mojom::internal::FrameHost_CreateNewWindow_Params_Data> params( message); params.Allocate(); mojo::internal::MessageFragment< typename decltype(params->params)::BaseType> params_fragment( params.message()); mojo::internal::Serialize<::content::mojom::CreateNewWindowParamsDataView>( param_params, params_fragment); params->params.Set( params_fragment.is_null() ? nullptr : params_fragment.data()); MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( params->params.is_null(), mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, "null params in FrameHost.CreateNewWindow request");#if defined(ENABLE_IPC_FUZZER) message.set_interface_name(FrameHost::Name_); message.set_method_name("CreateNewWindow");#endif bool result = false; std::unique_ptr<mojo::MessageReceiver> responder( new FrameHost_CreateNewWindow_HandleSyncResponse( &result, out_param_status, out_param_reply)); ::mojo::internal::SendMojoMessage(*receiver_, message, std::move(responder));#if BUILDFLAG(MOJO_TRACE_ENABLED) TRACE_EVENT_END1( "mojom", "FrameHost::CreateNewWindow", "sync_response_parameters", [&](perfetto::TracedValue context){ auto dict = std::move(context).WriteDictionary(); perfetto::WriteIntoTracedValueWithFallback( dict.AddItem("status"), out_param_status, "<value of type CreateNewWindowStatus>"); perfetto::WriteIntoTracedValueWithFallback( dict.AddItem("reply"), out_param_reply, "<value of type CreateNewWindowReplyPtr>"); });#endif return result;}
总结:
GET方式,会将表单中的数据(键值对)经过urlencode编码后追加到url中。