HTML 链接(Anchor)是网页之间跳转的核心部分。
HTML 使用链接与网络上的另一个文档相连。
HTML中的链接是一种用于在不同网页之间导航的元素。
链接通常用于将一个网页与另一个网页或资源(如文档、图像、音频文件等)相关联。
链接允许用户在浏览网页时单击文本或图像来跳转到其他位置,从而实现网页之间的互联。
HTML 链接 通过 <a> 标签创建,通常用于将用户从一个页面导航到另一个页面、从一个部分跳转到页面中的另一个部分、下载文件、打开电子邮件应用程序或执行 JavaScript 函数等。
基本语法
<a href="URL">链接文本</a>
<a>
标签:定义了一个超链接(anchor)。它是 HTML 中用来创建可点击链接的主要标签。href
属性:指定目标 URL,当点击链接时,浏览器将导航到此 URL。
示例代码
<a href="/index.html">本文本</a> 是一个指向本网站中的一个页面的链接。</p>
<p><a href="https://www.microsoft.com/">本文本</a> 是一个指向万维网上的页面的链接。</p>
那么前端点击链接打开url的时候 c++代码是如何实现的呢?
1、 html 链接(Anchor)接口定义:
third_party\blink\renderer\core\html\html_anchor_element.idl
/*
* Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// https://html.spec.whatwg.org/C/#the-a-element
[
Exposed=Window,
HTMLConstructor
] interface HTMLAnchorElement : HTMLElement {
[CEReactions, Reflect] attribute DOMString target;
[CEReactions, Reflect] attribute DOMString download;
[CEReactions, Reflect] attribute USVString ping;
[CEReactions, Reflect] attribute DOMString rel;
[SameObject, PutForwards=value] readonly attribute DOMTokenList relList;
[CEReactions, Reflect] attribute DOMString hreflang;
[RuntimeEnabled=HrefTranslate, CEReactions, Reflect] attribute DOMString hrefTranslate;
[CEReactions, Reflect] attribute DOMString type;
[CEReactions, Reflect, ReflectOnly=("","no-referrer","origin","no-referrer-when-downgrade","origin-when-cross-origin","unsafe-url"), ReflectMissing="", ReflectInvalid=""] attribute DOMString referrerPolicy;
[CEReactions, ImplementedAs=textContent] attribute DOMString text;
// obsolete members
// https://html.spec.whatwg.org/C/#HTMLAnchorElement-partial
[CEReactions, Reflect] attribute DOMString coords;
[CEReactions, Reflect] attribute DOMString charset;
[CEReactions, Reflect] attribute DOMString name;
[CEReactions, Reflect] attribute DOMString rev;
[CEReactions, Reflect] attribute DOMString shape;
};
HTMLAnchorElement includes HTMLAttributionSrcElementUtils;
HTMLAnchorElement includes HTMLHyperlinkElementUtils;
2、 html 链接(Anchor)接口实现:
third_party\blink\renderer\core\html\html_anchor_element.h
third_party\blink\renderer\core\html\html_anchor_element.cc
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2000 Simon Hausmann <hausmann@kde.org>
* Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_ANCHOR_ELEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_ANCHOR_ELEMENT_H_
#include "base/time/time.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/html/rel_list.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/loader/navigation_policy.h"
#include "third_party/blink/renderer/core/url/dom_url_utils.h"
#include "third_party/blink/renderer/platform/link_hash.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
namespace blink {
// Link relation bitmask values.
// FIXME: Uncomment as the various link relations are implemented.
enum {
// RelationAlternate = 0x00000001,
// RelationArchives = 0x00000002,
// RelationAuthor = 0x00000004,
// RelationBoomark = 0x00000008,
// RelationExternal = 0x00000010,
// RelationFirst = 0x00000020,
// RelationHelp = 0x00000040,
// RelationIndex = 0x00000080,
// RelationLast = 0x00000100,
// RelationLicense = 0x00000200,
// RelationNext = 0x00000400,
// RelationNoFolow = 0x00000800,
kRelationNoReferrer = 0x00001000,
// RelationPrev = 0x00002000,
// RelationSearch = 0x00004000,
// RelationSidebar = 0x00008000,
// RelationTag = 0x00010000,
// RelationUp = 0x00020000,
kRelationNoOpener = 0x00040000,
kRelationOpener = 0x00080000,
kRelationPrivacyPolicy = 0x00100000,
kRelationTermsOfService = 0x00200000,
};
class CORE_EXPORT HTMLAnchorElement : public HTMLElement, public DOMURLUtils {
DEFINE_WRAPPERTYPEINFO();
public:
HTMLAnchorElement(Document& document);
HTMLAnchorElement(const QualifiedName&, Document&);
~HTMLAnchorElement() override;
KURL Href() const;
void SetHref(const AtomicString&);
void setHref(const String&);
const AtomicString& GetName() const;
// Returns the anchor's |target| attribute, unless it is empty, in which case
// the BaseTarget from the document is returned.
const AtomicString& GetEffectiveTarget() const;
KURL Url() const final;
void SetURL(const KURL&) final;
String Input() const final;
bool IsLiveLink() const final;
bool WillRespondToMouseClickEvents() final;
bool HasRel(uint32_t relation) const;
void SetRel(const AtomicString&);
DOMTokenList& relList() const {
return static_cast<DOMTokenList&>(*rel_list_);
}
LinkHash VisitedLinkHash() const;
void InvalidateCachedVisitedLinkHash() { cached_visited_link_hash_ = 0; }
void SendPings(const KURL& destination_url) const;
// Element overrides:
void SetHovered(bool hovered) override;
void Trace(Visitor*) const override;
protected:
void ParseAttribute(const AttributeModificationParams&) override;
bool SupportsFocus(UpdateBehavior update_behavior =
UpdateBehavior::kStyleAndLayout) const override;
private:
void AttributeChanged(const AttributeModificationParams&) override;
bool ShouldHaveFocusAppearance() const final;
bool IsFocusable(UpdateBehavior update_behavior =
UpdateBehavior::kStyleAndLayout) const override;
bool IsKeyboardFocusable(UpdateBehavior update_behavior =
UpdateBehavior::kStyleAndLayout) const override;
void DefaultEventHandler(Event&) final;
bool HasActivationBehavior() const override;
void SetActive(bool active) final;
bool IsURLAttribute(const Attribute&) const final;
bool HasLegalLinkAttribute(const QualifiedName&) const final;
bool CanStartSelection() const final;
int DefaultTabIndex() const final;
bool draggable() const final;
bool IsInteractiveContent() const final;
InsertionNotificationRequest InsertedInto(ContainerNode&) override;
void RemovedFrom(ContainerNode&) override;
void NavigateToHyperlink(ResourceRequest,
NavigationPolicy,
bool is_trusted,
base::TimeTicks platform_time_stamp,
KURL);
void HandleClick(Event&);
unsigned link_relations_ : 31;
mutable LinkHash cached_visited_link_hash_;
Member<RelList> rel_list_;
};
inline LinkHash HTMLAnchorElement::VisitedLinkHash() const {
if (!cached_visited_link_hash_) {
cached_visited_link_hash_ = blink::VisitedLinkHash(
GetDocument().BaseURL(), FastGetAttribute(html_names::kHrefAttr));
}
return cached_visited_link_hash_;
}
// Functions shared with the other anchor elements (i.e., SVG).
bool IsEnterKeyKeydownEvent(Event&);
bool IsLinkClick(Event&);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_ANCHOR_ELEMENT_H_
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2000 Simon Hausmann <hausmann@kde.org>
* Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights
* reserved.
* (C) 2006 Graham Dennis (graham.dennis@gmail.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/html/html_anchor_element.h"
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/navigation/impression.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/events/pointer_event.h"
#include "third_party/blink/renderer/core/frame/ad_tracker.h"
#include "third_party/blink/renderer/core/frame/attribution_src_loader.h"
#include "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/anchor_element_metrics_sender.h"
#include "third_party/blink/renderer/core/html/anchor_element_observer_for_service_worker.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/loader/frame_load_request.h"
#include "third_party/blink/renderer/core/loader/navigation_policy.h"
#include "third_party/blink/renderer/core/loader/ping_loader.h"
#include "third_party/blink/renderer/core/navigation_api/navigation_api.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/timer.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
#include "ui/events/event_constants.h"
#include "ui/gfx/geometry/point_conversions.h"
namespace blink {
namespace {
// The download attribute specifies a filename, and an excessively long one can
// crash the browser process. Filepaths probably can't be longer than 4096
// characters, but this is enough to prevent the browser process from becoming
// unresponsive or crashing.
const int kMaxDownloadAttrLength = 1000000;
// Note: Here it covers download originated from clicking on <a download> link
// that results in direct download. Features in this method can also be logged
// from browser for download due to navigations to non-web-renderable content.
bool ShouldInterveneDownloadByFramePolicy(LocalFrame* frame) {
bool should_intervene_download = false;
Document& document = *(frame->GetDocument());
UseCounter::Count(document, WebFeature::kDownloadPrePolicyCheck);
bool has_gesture = LocalFrame::HasTransientUserActivation(frame);
if (!has_gesture) {
UseCounter::Count(document, WebFeature::kDownloadWithoutUserGesture);
}
if (frame->IsAdFrame()) {
UseCounter::Count(document, WebFeature::kDownloadInAdFrame);
if (!has_gesture) {
UseCounter::Count(document,
WebFeature::kDownloadInAdFrameWithoutUserGesture);
if (base::FeatureList::IsEnabled(
blink::features::
kBlockingDownloadsInAdFrameWithoutUserActivation))
should_intervene_download = true;
}
}
if (frame->DomWindow()->IsSandboxed(
network::mojom::blink::WebSandboxFlags::kDownloads)) {
UseCounter::Count(document, WebFeature::kDownloadInSandbox);
should_intervene_download = true;
}
if (!should_intervene_download)
UseCounter::Count(document, WebFeature::kDownloadPostPolicyCheck);
return should_intervene_download;
}
} // namespace
HTMLAnchorElement::HTMLAnchorElement(Document& document)
: HTMLAnchorElement(html_names::kATag, document) {}
HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tag_name,
Document& document)
: HTMLElement(tag_name, document),
link_relations_(0),
cached_visited_link_hash_(0),
rel_list_(MakeGarbageCollected<RelList>(this)) {}
HTMLAnchorElement::~HTMLAnchorElement() = default;
bool HTMLAnchorElement::SupportsFocus(UpdateBehavior update_behavior) const {
if (IsLink() && !IsEditable(*this)) {
return true;
}
return HTMLElement::SupportsFocus(update_behavior);
}
bool HTMLAnchorElement::ShouldHaveFocusAppearance() const {
// TODO(crbug.com/1444450): Can't this be done with focus-visible now?
return (GetDocument().LastFocusType() != mojom::blink::FocusType::kMouse) ||
HTMLElement::SupportsFocus(UpdateBehavior::kNoneForIsFocused);
}
bool HTMLAnchorElement::IsFocusable(UpdateBehavior update_behavior) const {
if (!IsFocusableStyle(update_behavior)) {
return false;
}
if (IsLink()) {
return SupportsFocus(update_behavior);
}
return HTMLElement::IsFocusable(update_behavior);
}
bool HTMLAnchorElement::IsKeyboardFocusable(
UpdateBehavior update_behavior) const {
if (!IsFocusableStyle(update_behavior)) {
return false;
}
// Anchor is focusable if the base element supports focus and is focusable.
if (Element::SupportsFocus(update_behavior) && IsFocusable(update_behavior)) {
return HTMLElement::IsKeyboardFocusable(update_behavior);
}
if (IsLink() && !GetDocument().GetPage()->GetChromeClient().TabsToLinks())
return false;
return HTMLElement::IsKeyboardFocusable(update_behavior);
}
static void AppendServerMapMousePosition(StringBuilder& url, Event* event) {
auto* mouse_event = DynamicTo<MouseEvent>(event);
if (!mouse_event)
return;
DCHECK(event->target());
Node* target = event->target()->ToNode();
DCHECK(target);
auto* image_element = DynamicTo<HTMLImageElement>(target);
if (!image_element || !image_element->IsServerMap())
return;
LayoutObject* layout_object = image_element->GetLayoutObject();
if (!layout_object || !layout_object->IsBox())
return;
// The coordinates sent in the query string are relative to the height and
// width of the image element, ignoring CSS transform/zoom.
gfx::PointF map_point =
layout_object->AbsoluteToLocalPoint(mouse_event->AbsoluteLocation());
// The origin (0,0) is at the upper left of the content area, inside the
// padding and border.
map_point -=
gfx::Vector2dF(To<LayoutBox>(layout_object)->PhysicalContentBoxOffset());
// CSS zoom is not reflected in the map coordinates.
float scale_factor = 1 / layout_object->Style()->EffectiveZoom();
map_point.Scale(scale_factor, scale_factor);
// Negative coordinates are clamped to 0 such that clicks in the left and
// top padding/border areas receive an X or Y coordinate of 0.
gfx::Point clamped_point = gfx::ToRoundedPoint(map_point);
clamped_point.SetToMax(gfx::Point());
url.Append('?');
url.AppendNumber(clamped_point.x());
url.Append(',');
url.AppendNumber(clamped_point.y());
}
void HTMLAnchorElement::DefaultEventHandler(Event& event) {
if (IsLink()) {
if (isConnected() && base::FeatureList::IsEnabled(
features::kSpeculativeServiceWorkerWarmUp)) {
Document& top_document = GetDocument().TopDocument();
if (auto* observer =
AnchorElementObserverForServiceWorker::From(top_document)) {
if (features::kSpeculativeServiceWorkerWarmUpOnPointerover.Get() &&
(event.type() == event_type_names::kMouseover ||
event.type() == event_type_names::kPointerover)) {
observer->MaybeSendNavigationTargetLinks({this});
} else if (features::kSpeculativeServiceWorkerWarmUpOnPointerdown
.Get() &&
(event.type() == event_type_names::kMousedown ||
event.type() == event_type_names::kPointerdown ||
event.type() == event_type_names::kTouchstart)) {
observer->MaybeSendNavigationTargetLinks({this});
}
}
}
if (IsFocused() && IsEnterKeyKeydownEvent(event) && IsLiveLink()) {
event.SetDefaultHandled();
DispatchSimulatedClick(&event);
return;
}
if (IsLinkClick(event) && IsLiveLink()) {
HandleClick(event);
return;
}
}
HTMLElement::DefaultEventHandler(event);
}
bool HTMLAnchorElement::HasActivationBehavior() const {
return IsLink();
}
void HTMLAnchorElement::SetActive(bool active) {
if (active && IsEditable(*this))
return;
HTMLElement::SetActive(active);
}
void HTMLAnchorElement::AttributeChanged(
const AttributeModificationParams& params) {
HTMLElement::AttributeChanged(params);
if (params.reason != AttributeModificationReason::kDirectly)
return;
if (params.name != html_names::kHrefAttr)
return;
if (!IsLink() && AdjustedFocusedElementInTreeScope() == this)
blur();
}
void HTMLAnchorElement::ParseAttribute(
const AttributeModificationParams& params) {
if (params.name == html_names::kHrefAttr) {
if (params.old_value == params.new_value) {
return;
}
bool was_link = IsLink();
SetIsLink(!params.new_value.IsNull());
if (was_link || IsLink()) {
PseudoStateChanged(CSSSelector::kPseudoLink);
PseudoStateChanged(CSSSelector::kPseudoVisited);
if (was_link != IsLink()) {
PseudoStateChanged(CSSSelector::kPseudoWebkitAnyLink);
PseudoStateChanged(CSSSelector::kPseudoAnyLink);
}
}
if (isConnected() && params.old_value != params.new_value) {
if (auto* document_rules =
DocumentSpeculationRules::FromIfExists(GetDocument())) {
document_rules->HrefAttributeChanged(this, params.old_value,
params.new_value);
}
}
InvalidateCachedVisitedLinkHash();
LogUpdateAttributeIfIsolatedWorldAndInDocument("a", params);
} else if (params.name == html_names::kNameAttr ||
params.name == html_names::kTitleAttr) {
// Do nothing.
} else if (params.name == html_names::kRelAttr) {
SetRel(params.new_value);
rel_list_->DidUpdateAttributeValue(params.old_value, params.new_value);
if (isConnected() && IsLink() && params.old_value != params.new_value) {
if (auto* document_rules =
DocumentSpeculationRules::FromIfExists(GetDocument())) {
document_rules->RelAttributeChanged(this);
}
}
} else if (params.name == html_names::kReferrerpolicyAttr) {
if (isConnected() && IsLink() && params.old_value != params.new_value) {
if (auto* document_rules =
DocumentSpeculationRules::FromIfExists(GetDocument())) {
document_rules->ReferrerPolicyAttributeChanged(this);
}
}
} else if (params.name == html_names::kTargetAttr) {
if (isConnected() && IsLink() && params.old_value != params.new_value) {
if (auto* document_rules =
DocumentSpeculationRules::FromIfExists(GetDocument())) {
document_rules->TargetAttributeChanged(this);
}
}
} else {
HTMLElement::ParseAttribute(params);
}
}
bool HTMLAnchorElement::IsURLAttribute(const Attribute& attribute) const {
return attribute.GetName().LocalName() == html_names::kHrefAttr ||
HTMLElement::IsURLAttribute(attribute);
}
bool HTMLAnchorElement::HasLegalLinkAttribute(const QualifiedName& name) const {
return name == html_names::kHrefAttr ||
HTMLElement::HasLegalLinkAttribute(name);
}
bool HTMLAnchorElement::CanStartSelection() const {
if (!IsLink())
return HTMLElement::CanStartSelection();
return IsEditable(*this);
}
bool HTMLAnchorElement::draggable() const {
// Should be draggable if we have an href attribute.
const AtomicString& value = FastGetAttribute(html_names::kDraggableAttr);
if (EqualIgnoringASCIICase(value, "true"))
return true;
if (EqualIgnoringASCIICase(value, "false"))
return false;
return FastHasAttribute(html_names::kHrefAttr);
}
KURL HTMLAnchorElement::Href() const {
return GetDocument().CompleteURL(StripLeadingAndTrailingHTMLSpaces(
FastGetAttribute(html_names::kHrefAttr)));
}
void HTMLAnchorElement::SetHref(const AtomicString& value) {
setAttribute(html_names::kHrefAttr, value);
}
KURL HTMLAnchorElement::Url() const {
KURL href = Href();
if (!href.IsValid()) {
return KURL();
}
return href;
}
void HTMLAnchorElement::SetURL(const KURL& url) {
SetHref(AtomicString(url.GetString()));
}
String HTMLAnchorElement::Input() const {
return FastGetAttribute(html_names::kHrefAttr);
}
void HTMLAnchorElement::setHref(const String& value) {
SetHref(AtomicString(value));
}
bool HTMLAnchorElement::HasRel(uint32_t relation) const {
return link_relations_ & relation;
}
void HTMLAnchorElement::SetRel(const AtomicString& value) {
link_relations_ = 0;
SpaceSplitString new_link_relations(value.LowerASCII());
// FIXME: Add link relations as they are implemented
if (new_link_relations.Contains(AtomicString("noreferrer"))) {
link_relations_ |= kRelationNoReferrer;
}
if (new_link_relations.Contains(AtomicString("noopener"))) {
link_relations_ |= kRelationNoOpener;
}
if (new_link_relations.Contains(AtomicString("opener"))) {
link_relations_ |= kRelationOpener;
UseCounter::Count(GetDocument(), WebFeature::kLinkRelOpener);
}
// These don't currently have web-facing behavior, but embedders may wish to
// expose their presence to users:
if (new_link_relations.Contains(AtomicString("privacy-policy"))) {
link_relations_ |= kRelationPrivacyPolicy;
UseCounter::Count(GetDocument(), WebFeature::kLinkRelPrivacyPolicy);
}
if (new_link_relations.Contains(AtomicString("terms-of-service"))) {
link_relations_ |= kRelationTermsOfService;
UseCounter::Count(GetDocument(), WebFeature::kLinkRelTermsOfService);
}
// Adding or removing a value here whose processing model is web-visible
// (e.g. if the value is listed as a "supported token" for `<a>`'s `rel`
// attribute in HTML) also requires you to update the list of tokens in
// RelList::SupportedTokensAnchorAndAreaAndForm().
}
const AtomicString& HTMLAnchorElement::GetName() const {
return GetNameAttribute();
}
const AtomicString& HTMLAnchorElement::GetEffectiveTarget() const {
const AtomicString& target = FastGetAttribute(html_names::kTargetAttr);
if (!target.empty())
return target;
return GetDocument().BaseTarget();
}
int HTMLAnchorElement::DefaultTabIndex() const {
return 0;
}
bool HTMLAnchorElement::IsLiveLink() const {
return IsLink() && !IsEditable(*this);
}
void HTMLAnchorElement::SendPings(const KURL& destination_url) const {
const AtomicString& ping_value = FastGetAttribute(html_names::kPingAttr);
if (ping_value.IsNull() || !GetDocument().GetSettings() ||
!GetDocument().GetSettings()->GetHyperlinkAuditingEnabled()) {
return;
}
// Pings should not be sent if MHTML page is loaded.
if (GetDocument().Fetcher()->Archive())
return;
if ((ping_value.Contains('\n') || ping_value.Contains('\r') ||
ping_value.Contains('\t')) &&
ping_value.Contains('<')) {
Deprecation::CountDeprecation(
GetExecutionContext(), WebFeature::kCanRequestURLHTTPContainingNewline);
return;
}
UseCounter::Count(GetDocument(), WebFeature::kHTMLAnchorElementPingAttribute);
SpaceSplitString ping_urls(ping_value);
for (unsigned i = 0; i < ping_urls.size(); i++) {
PingLoader::SendLinkAuditPing(GetDocument().GetFrame(),
GetDocument().CompleteURL(ping_urls[i]),
destination_url);
}
}
void HTMLAnchorElement::NavigateToHyperlink(ResourceRequest request,
NavigationPolicy navigation_policy,
bool is_trusted,
base::TimeTicks platform_time_stamp,
KURL completed_url) {
LocalDOMWindow* window = GetDocument().domWindow();
if (!window) {
return;
}
LocalFrame* frame = window->GetFrame();
if (!frame) {
return;
}
if (navigation_policy == kNavigationPolicyLinkPreview) {
// Ensured by third_party/blink/renderer/core/loader/navigation_policy.cc.
CHECK(base::FeatureList::IsEnabled(features::kLinkPreview));
DocumentSpeculationRules::From(GetDocument()).InitiatePreview(Url());
return;
}
request.SetRequestContext(mojom::blink::RequestContextType::HYPERLINK);
FrameLoadRequest frame_request(window, request);
frame_request.SetNavigationPolicy(navigation_policy);
frame_request.SetClientRedirectReason(ClientNavigationReason::kAnchorClick);
frame_request.SetSourceElement(this);
const AtomicString& target =
frame_request.CleanNavigationTarget(GetEffectiveTarget());
if (HasRel(kRelationNoReferrer)) {
frame_request.SetNoReferrer();
frame_request.SetNoOpener();
}
if (HasRel(kRelationNoOpener) ||
(EqualIgnoringASCIICase(target, "_blank") && !HasRel(kRelationOpener) &&
frame->GetSettings()
->GetTargetBlankImpliesNoOpenerEnabledWillBeRemoved())) {
frame_request.SetNoOpener();
}
frame_request.SetTriggeringEventInfo(
is_trusted ? mojom::blink::TriggeringEventInfo::kFromTrustedEvent
: mojom::blink::TriggeringEventInfo::kFromUntrustedEvent);
frame_request.SetInputStartTime(platform_time_stamp);
if (const AtomicString& attribution_src =
FastGetAttribute(html_names::kAttributionsrcAttr);
!attribution_src.IsNull()) {
// An impression must be attached prior to the
// `FindOrCreateFrameForNavigation()` call, as that call may result in
// performing a navigation if the call results in creating a new window with
// noopener set.
// At this time we don't know if the navigation will navigate a main frame
// or subframe. For example, a middle click on the anchor element will
// set `target_frame` to `frame`, but end up targeting a new window.
// Attach the impression regardless, the embedder will be able to drop
// impressions for subframe navigations.
frame_request.SetImpression(
frame->GetAttributionSrcLoader()->RegisterNavigation(
/*navigation_url=*/completed_url, attribution_src,
/*element=*/this, request.HasUserGesture()));
}
Frame* target_frame =
frame->Tree().FindOrCreateFrameForNavigation(frame_request, target).frame;
// If hrefTranslate is enabled and set restrict processing it
// to same frame or navigations with noopener set.
if (RuntimeEnabledFeatures::HrefTranslateEnabled(GetExecutionContext()) &&
FastHasAttribute(html_names::kHreftranslateAttr) &&
(target_frame == frame || frame_request.GetWindowFeatures().noopener)) {
frame_request.SetHrefTranslate(
FastGetAttribute(html_names::kHreftranslateAttr));
UseCounter::Count(GetDocument(),
WebFeature::kHTMLAnchorElementHrefTranslateAttribute);
}
if (target_frame == frame && HasRel(kRelationOpener)) {
// TODO(https://crbug.com/1431495): rel=opener is currently only meaningful
// with target=_blank. Applying it to same-frame navigations is a potential
// opt-out for issue 1431495, but how many sites would trigger this opt-out
// inadvertently?
UseCounter::Count(GetDocument(),
WebFeature::kLinkRelOpenerTargetingSameFrame);
}
if (target_frame) {
target_frame->Navigate(frame_request, WebFrameLoadType::kStandard);
}
}
void HTMLAnchorElement::SetHovered(bool hovered) {
HTMLElement::SetHovered(hovered);
}
void HTMLAnchorElement::HandleClick(Event& event) {
event.SetDefaultHandled();
LocalDOMWindow* window = GetDocument().domWindow();
if (!window)
return;
if (!isConnected()) {
UseCounter::Count(GetDocument(),
WebFeature::kAnchorClickDispatchForNonConnectedNode);
}
Document& top_document = GetDocument().TopDocument();
if (auto* sender = AnchorElementMetricsSender::From(top_document)) {
sender->MaybeReportClickedMetricsOnClick(*this);
}
StringBuilder url;
url.Append(StripLeadingAndTrailingHTMLSpaces(
FastGetAttribute(html_names::kHrefAttr)));
AppendServerMapMousePosition(url, &event);
KURL completed_url = GetDocument().CompleteURL(url.ToString());
// Schedule the ping before the frame load. Prerender in Chrome may kill the
// renderer as soon as the navigation is sent out.
SendPings(completed_url);
ResourceRequest request(completed_url);
network::mojom::ReferrerPolicy policy;
if (FastHasAttribute(html_names::kReferrerpolicyAttr) &&
SecurityPolicy::ReferrerPolicyFromString(
FastGetAttribute(html_names::kReferrerpolicyAttr),
kSupportReferrerPolicyLegacyKeywords, &policy) &&
!HasRel(kRelationNoReferrer)) {
UseCounter::Count(GetDocument(),
WebFeature::kHTMLAnchorElementReferrerPolicyAttribute);
request.SetReferrerPolicy(policy);
}
LocalFrame* frame = window->GetFrame();
request.SetHasUserGesture(LocalFrame::HasTransientUserActivation(frame));
NavigationPolicy navigation_policy = NavigationPolicyFromEvent(&event);
// Respect the download attribute only if we can read the content, and the
// event is not an alt-click or similar.
if (FastHasAttribute(html_names::kDownloadAttr) &&
navigation_policy != kNavigationPolicyDownload &&
window->GetSecurityOrigin()->CanReadContent(completed_url)) {
if (ShouldInterveneDownloadByFramePolicy(frame))
return;
String download_attr =
static_cast<String>(FastGetAttribute(html_names::kDownloadAttr));
if (download_attr.length() > kMaxDownloadAttrLength) {
ConsoleMessage* console_message = MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kRendering,
mojom::blink::ConsoleMessageLevel::kError,
String::Format("Download attribute for anchor element is too long. "
"Max: %d, given: %d",
kMaxDownloadAttrLength, download_attr.length()));
console_message->SetNodes(GetDocument().GetFrame(),
{this->GetDomNodeId()});
GetDocument().AddConsoleMessage(console_message);
return;
}
auto* params = MakeGarbageCollected<NavigateEventDispatchParams>(
completed_url, NavigateEventType::kCrossDocument,
WebFrameLoadType::kStandard);
if (event.isTrusted())
params->involvement = UserNavigationInvolvement::kActivation;
params->download_filename = download_attr;
params->source_element = this;
if (window->navigation()->DispatchNavigateEvent(params) !=
NavigationApi::DispatchResult::kContinue) {
return;
}
// A download will never notify blink about its completion. Tell the
// NavigationApi that the navigation was dropped, so that it doesn't
// leave the frame thinking it is loading indefinitely.
window->navigation()->InformAboutCanceledNavigation(
CancelNavigationReason::kDropped);
request.SetSuggestedFilename(download_attr);
request.SetRequestContext(mojom::blink::RequestContextType::DOWNLOAD);
request.SetRequestorOrigin(window->GetSecurityOrigin());
network::mojom::ReferrerPolicy referrer_policy =
request.GetReferrerPolicy();
if (referrer_policy == network::mojom::ReferrerPolicy::kDefault)
referrer_policy = window->GetReferrerPolicy();
Referrer referrer = SecurityPolicy::GenerateReferrer(
referrer_policy, completed_url, window->OutgoingReferrer());
request.SetReferrerString(referrer.referrer);
request.SetReferrerPolicy(referrer.referrer_policy);
frame->DownloadURL(request, network::mojom::blink::RedirectMode::kManual);
return;
}
base::OnceClosure navigate_closure = WTF::BindOnce(
&HTMLAnchorElement::NavigateToHyperlink, WrapWeakPersistent(this),
std::move(request), navigation_policy, event.isTrusted(),
event.PlatformTimeStamp(), std::move(completed_url));
if (navigation_policy == kNavigationPolicyDownload ||
navigation_policy == kNavigationPolicyLinkPreview) {
// We distinguish single/double click with some modifiers.
// See the comment of `EventHandler.delayed_navigation_task_handle_`.
auto task_handle = PostDelayedCancellableTask(
*base::SingleThreadTaskRunner::GetCurrentDefault(), FROM_HERE,
std::move(navigate_closure),
base::Milliseconds(ui::kDoubleClickTimeMs));
frame->GetEventHandler().SetDelayedNavigationTaskHandle(
std::move(task_handle));
} else {
std::move(navigate_closure).Run();
}
}
bool IsEnterKeyKeydownEvent(Event& event) {
auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
return event.type() == event_type_names::kKeydown && keyboard_event &&
keyboard_event->key() == "Enter" && !keyboard_event->repeat();
}
bool IsLinkClick(Event& event) {
auto* mouse_event = DynamicTo<MouseEvent>(event);
if ((event.type() != event_type_names::kClick &&
event.type() != event_type_names::kAuxclick) ||
!mouse_event) {
return false;
}
int16_t button = mouse_event->button();
return (button == static_cast<int16_t>(WebPointerProperties::Button::kLeft) ||
button ==
static_cast<int16_t>(WebPointerProperties::Button::kMiddle));
}
bool HTMLAnchorElement::WillRespondToMouseClickEvents() {
return IsLink() || HTMLElement::WillRespondToMouseClickEvents();
}
bool HTMLAnchorElement::IsInteractiveContent() const {
return IsLink();
}
Node::InsertionNotificationRequest HTMLAnchorElement::InsertedInto(
ContainerNode& insertion_point) {
InsertionNotificationRequest request =
HTMLElement::InsertedInto(insertion_point);
LogAddElementIfIsolatedWorldAndInDocument("a", html_names::kHrefAttr);
Document& top_document = GetDocument().TopDocument();
if (auto* sender = AnchorElementMetricsSender::From(top_document)) {
sender->AddAnchorElement(*this);
}
if (isConnected() && IsLink()) {
static const bool speculative_service_worker_warm_up_enabled =
base::FeatureList::IsEnabled(features::kSpeculativeServiceWorkerWarmUp);
if (speculative_service_worker_warm_up_enabled) {
static const bool warm_up_on_visible =
features::kSpeculativeServiceWorkerWarmUpOnVisible.Get();
static const bool warm_up_on_inserted_into_dom =
features::kSpeculativeServiceWorkerWarmUpOnInsertedIntoDom.Get();
if (warm_up_on_visible || warm_up_on_inserted_into_dom) {
if (auto* observer =
AnchorElementObserverForServiceWorker::From(top_document)) {
if (warm_up_on_visible) {
observer->ObserveAnchorElementVisibility(*this);
}
if (warm_up_on_inserted_into_dom) {
observer->MaybeSendNavigationTargetLinks({this});
}
}
}
}
if (auto* document_rules =
DocumentSpeculationRules::FromIfExists(GetDocument())) {
document_rules->LinkInserted(this);
}
}
return request;
}
void HTMLAnchorElement::RemovedFrom(ContainerNode& insertion_point) {
HTMLElement::RemovedFrom(insertion_point);
if (insertion_point.isConnected() && IsLink()) {
if (auto* document_rules =
DocumentSpeculationRules::FromIfExists(GetDocument())) {
document_rules->LinkRemoved(this);
}
}
}
void HTMLAnchorElement::Trace(Visitor* visitor) const {
visitor->Trace(rel_list_);
HTMLElement::Trace(visitor);
}
} // namespace blink
3、点击链接的关键处理函数void HTMLAnchorElement::HandleClick(Event& event) {
event.SetDefaultHandled();
LocalDOMWindow* window = GetDocument().domWindow();
if (!window)
return;
if (!isConnected()) {
UseCounter::Count(GetDocument(),
WebFeature::kAnchorClickDispatchForNonConnectedNode);
}
Document& top_document = GetDocument().TopDocument();
if (auto* sender = AnchorElementMetricsSender::From(top_document)) {
sender->MaybeReportClickedMetricsOnClick(*this);
}
//获取href属性值 【html_names::kHrefAttr】
StringBuilder url;
url.Append(StripLeadingAndTrailingHTMLSpaces(
FastGetAttribute(html_names::kHrefAttr)));
AppendServerMapMousePosition(url, &event);
KURL completed_url = GetDocument().CompleteURL(url.ToString());
// Schedule the ping before the frame load. Prerender in Chrome may kill the
// renderer as soon as the navigation is sent out.
SendPings(completed_url);
ResourceRequest request(completed_url);
network::mojom::ReferrerPolicy policy;
if (FastHasAttribute(html_names::kReferrerpolicyAttr) &&
SecurityPolicy::ReferrerPolicyFromString(
FastGetAttribute(html_names::kReferrerpolicyAttr),
kSupportReferrerPolicyLegacyKeywords, &policy) &&
!HasRel(kRelationNoReferrer)) {
UseCounter::Count(GetDocument(),
WebFeature::kHTMLAnchorElementReferrerPolicyAttribute);
request.SetReferrerPolicy(policy);
}
LocalFrame* frame = window->GetFrame();
request.SetHasUserGesture(LocalFrame::HasTransientUserActivation(frame));
NavigationPolicy navigation_policy = NavigationPolicyFromEvent(&event);
// Respect the download attribute only if we can read the content, and the
// event is not an alt-click or similar.
if (FastHasAttribute(html_names::kDownloadAttr) &&
navigation_policy != kNavigationPolicyDownload &&
window->GetSecurityOrigin()->CanReadContent(completed_url)) {
if (ShouldInterveneDownloadByFramePolicy(frame))
return;
String download_attr =
static_cast<String>(FastGetAttribute(html_names::kDownloadAttr));
if (download_attr.length() > kMaxDownloadAttrLength) {
ConsoleMessage* console_message = MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kRendering,
mojom::blink::ConsoleMessageLevel::kError,
String::Format("Download attribute for anchor element is too long. "
"Max: %d, given: %d",
kMaxDownloadAttrLength, download_attr.length()));
console_message->SetNodes(GetDocument().GetFrame(),
{this->GetDomNodeId()});
GetDocument().AddConsoleMessage(console_message);
return;
}
auto* params = MakeGarbageCollected<NavigateEventDispatchParams>(
completed_url, NavigateEventType::kCrossDocument,
WebFrameLoadType::kStandard);
if (event.isTrusted())
params->involvement = UserNavigationInvolvement::kActivation;
params->download_filename = download_attr;
params->source_element = this;
if (window->navigation()->DispatchNavigateEvent(params) !=
NavigationApi::DispatchResult::kContinue) {
return;
}
// A download will never notify blink about its completion. Tell the
// NavigationApi that the navigation was dropped, so that it doesn't
// leave the frame thinking it is loading indefinitely.
window->navigation()->InformAboutCanceledNavigation(
CancelNavigationReason::kDropped);
request.SetSuggestedFilename(download_attr);
request.SetRequestContext(mojom::blink::RequestContextType::DOWNLOAD);
request.SetRequestorOrigin(window->GetSecurityOrigin());
network::mojom::ReferrerPolicy referrer_policy =
request.GetReferrerPolicy();
if (referrer_policy == network::mojom::ReferrerPolicy::kDefault)
referrer_policy = window->GetReferrerPolicy();
Referrer referrer = SecurityPolicy::GenerateReferrer(
referrer_policy, completed_url, window->OutgoingReferrer());
request.SetReferrerString(referrer.referrer);
request.SetReferrerPolicy(referrer.referrer_policy);
frame->DownloadURL(request, network::mojom::blink::RedirectMode::kManual);
return;
}
base::OnceClosure navigate_closure = WTF::BindOnce(
&HTMLAnchorElement::NavigateToHyperlink, WrapWeakPersistent(this),
std::move(request), navigation_policy, event.isTrusted(),
event.PlatformTimeStamp(), std::move(completed_url));
if (navigation_policy == kNavigationPolicyDownload ||
navigation_policy == kNavigationPolicyLinkPreview) {
// We distinguish single/double click with some modifiers.
// See the comment of `EventHandler.delayed_navigation_task_handle_`.
auto task_handle = PostDelayedCancellableTask(
*base::SingleThreadTaskRunner::GetCurrentDefault(), FROM_HERE,
std::move(navigate_closure),
base::Milliseconds(ui::kDoubleClickTimeMs));
frame->GetEventHandler().SetDelayedNavigationTaskHandle(
std::move(task_handle));
} else {
std::move(navigate_closure).Run();
}
}
通过
StringBuilder url;
url.Append(StripLeadingAndTrailingHTMLSpaces(
FastGetAttribute(html_names::kHrefAttr)));
AppendServerMapMousePosition(url, &event);
KURL completed_url = GetDocument().CompleteURL(url.ToString());
解析出href值 。
其中html_names::kHrefAttr定义在third_party\blink\renderer\core\html\html_attribute_names.json5,
{
metadata: {
namespace: "HTML",
namespacePrefix: "xhtml",
namespaceURI: "http://www.w3.org/1999/xhtml",
attrsNullNamespace: true,
export: "CORE_EXPORT",
},
data: [
"abbr",
"accept-charset",
"accept",
"accesskey",
"action",
"adauctionheaders",
"align",
"alink",
"allow",
"allowfullscreen",
"allowpaymentrequest",
"alt",
"anchor",
"archive",
"as",
"async",
"attributionsrc",
"autocapitalize",
"autocomplete",
"autocorrect",
"autofocus",
"autoplay",
"axis",
"background",
"behavior",
"bgcolor",
"blocking",
"border",
"bordercolor",
"browsingtopics",
"capture",
"cellpadding",
"cellspacing",
"char",
"challenge",
"charoff",
"charset",
"checked",
"cite",
"class",
"classid",
"clear",
"code",
"codebase",
"codetype",
"color",
"cols",
"colspan",
"compact",
"content",
"contenteditable",
"controls",
"controlslist",
"coords",
"credentialless",
"crossorigin",
"csp",
"data",
"data-src",
"datetime",
"declare",
"decoding",
"default",
"defer",
"delegatesfocus",
"dir",
"direction",
"dirname",
"disabled",
"disablepictureinpicture",
"disableremoteplayback",
"download",
"draggable",
"elementtiming",
"enctype",
"end",
"enterkeyhint",
"event",
"exportparts",
"face",
"fetchpriority",
"focusgroup",
"for",
"form",
"formaction",
"formenctype",
"formmethod",
"formnovalidate",
"formtarget",
"frame",
"frameborder",
"headers",
"height",
"hidden",
"high",
"href",
"hreflang",
"hreftranslate",
"hspace",
"http-equiv",
"id",
"imagesizes",
"imagesrcset",
"incremental",
"inert",
"inputmode",
"integrity",
"invokeaction",
"invoketarget",
"is",
"ismap",
"itemprop",
"keytype",
"kind",
"invisible",
"label",
"lang",
"language",
"latencyhint",
"leftmargin",
"link",
"list",
"loading",
"longdesc",
"loop",
"low",
"lowsrc",
"manifest",
"marginheight",
"marginwidth",
"max",
"maxlength",
"mayscript",
"media",
"method",
"min",
"minlength",
"multiple",
"muted",
"name",
"nohref",
"nomodule",
"nonce",
"noresize",
"noshade",
"novalidate",
"nowrap",
"object",
"onabort",
"onafterprint",
"onanimationstart",
"onanimationiteration",
"onanimationend",
"onauxclick",
"onbeforecopy",
"onbeforecut",
"onbeforeinput",
"onbeforepaste",
"onbeforeprint",
"onbeforetoggle",
"onbeforeunload",
"onblur",
"oncancel",
"oncanplay",
"oncanplaythrough",
"onchange",
"onclick",
"onclose",
"oncontentvisibilityautostatechange",
"oncontextlost",
"oncontextmenu",
"oncontextrestored",
"oncopy",
"oncuechange",
"oncut",
"ondblclick",
"ondrag",
"ondragend",
"ondragenter",
"ondragleave",
"ondragover",
"ondragstart",
"ondrop",
"ondurationchange",
"onemptied",
"onended",
"onerror",
"onfocus",
"onfocusin",
"onfocusout",
"onformdata",
"ongotpointercapture",
"onhashchange",
"oninput",
"oninvalid",
"onkeydown",
"onkeypress",
"onkeyup",
"onlanguagechange",
"onload",
"onloadeddata",
"onloadedmetadata",
"onloadstart",
"onlostpointercapture",
"onmessage",
"onmessageerror",
"onmousedown",
"onmouseenter",
"onmouseleave",
"onmousemove",
"onmouseout",
"onmouseover",
"onmouseup",
"onmousewheel",
"onmove",
"ononline",
"onoffline",
"onorientationchange",
"onoverscroll",
"onpagehide",
"onpageshow",
"onpaste",
"onpause",
"onplay",
"onplaying",
"onpointercancel",
"onpointerdown",
"onpointerenter",
"onpointerleave",
"onpointermove",
"onpointerout",
"onpointerover",
"onpointerrawupdate",
"onpointerup",
"onpopstate",
"onprogress",
"onratechange",
"onreset",
"onresize",
"onscroll",
"onscrollend",
"onsearch",
"onsecuritypolicyviolation",
"onseeked",
"onseeking",
"onselect",
"onselectstart",
"onselectionchange",
"onshow",
"onslotchange",
"onsnapchanged",
"onsnapchanging",
"onstalled",
"onstorage",
"onsuspend",
"onsubmit",
"ontimeupdate",
"ontimezonechange",
"ontoggle",
"ontouchstart",
"ontouchmove",
"ontouchend",
"ontouchcancel",
"ontransitionend",
"onunload",
"onvolumechange",
"onwaiting",
"onwebkitanimationstart",
"onwebkitanimationiteration",
"onwebkitanimationend",
"onwebkitfullscreenchange",
"onwebkitfullscreenerror",
"onwebkittransitionend",
"onwheel",
"open",
"optimum",
"parseparts",
"part",
"pattern",
"placeholder",
"playsinline",
"ping",
"policy",
"popover",
"popovertarget",
"popovertargetaction",
"poster",
"preload",
"property",
"pseudo",
"readonly",
"referrerpolicy",
"rel",
"required",
"rev",
"reversed",
"role",
"rows",
"rowspan",
"rules",
"sandbox",
"scheme",
"scope",
"scrollamount",
"scrolldelay",
"scrolling",
"select",
"selected",
"shadowroot",
"shadowrootmode",
"shadowrootdelegatesfocus",
"shape",
"sharedstoragewritable",
"size",
"sizes",
"slot",
"span",
"spellcheck",
"src",
"srcset",
"srcdoc",
"srclang",
"standby",
"start",
"step",
"style",
"summary",
"tabindex",
"target",
"text",
"title",
"topmargin",
"translate",
"truespeed",
"privatetoken",
"type",
"usemap",
"valign",
"value",
"valuetype",
"version",
"vlink",
"vspace",
"virtualkeyboardpolicy",
"webkitdirectory",
"width",
"wrap",
],
}
对应c++代码实现在
out\Debug\gen\third_party\blink\renderer\core\html_names.cc
out\Debug\gen\third_party\blink\renderer\core\html_names.h
截取一部分html_names.h定义:
CORE_EXPORT extern const blink::QualifiedName& kHrefAttr;
CORE_EXPORT extern const blink::QualifiedName& kHreflangAttr;
CORE_EXPORT extern const blink::QualifiedName& kHreftranslateAttr;
4、看下调用顺序:
1)、HTMLAnchorElement::HandleClick(Event& event) 鼠标点击 解析出href="http://192.168.1.1//index.html"
2)、HTMLAnchorElement::NavigateToHyperlink函数发送
if (target_frame) {
target_frame->Navigate(frame_request, WebFrameLoadType::kStandard);
}请求
3)、LocalFrame::Navigate函数 (src\third_party\blink\renderer\core\frame\local_frame.cc)
void LocalFrame::Navigate(FrameLoadRequest& request,
WebFrameLoadType frame_load_type) {
if (HTMLFrameOwnerElement* element = DeprecatedLocalOwner())
element->CancelPendingLazyLoad();
if (!navigation_rate_limiter().CanProceed())
return;
TRACE_EVENT2("navigation", "LocalFrame::Navigate", "url",
request.GetResourceRequest().Url().GetString().Utf8(),
"load_type", static_cast<int>(frame_load_type));
if (request.ClientRedirectReason() != ClientNavigationReason::kNone)
probe::FrameScheduledNavigation(this, request.GetResourceRequest().Url(),
base::TimeDelta(),
request.ClientRedirectReason());
if (NavigationShouldReplaceCurrentHistoryEntry(request, frame_load_type))
frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
const ClientNavigationReason client_redirect_reason =
request.ClientRedirectReason();
loader_.StartNavigation(request, frame_load_type);
if (client_redirect_reason != ClientNavigationReason::kNone)
probe::FrameClearedScheduledNavigation(this);
}
发送loader_.StartNavigation(request, frame_load_type);请求。
4)、LocalFrameClientImpl::BeginNavigation()【src\third_party\blink\renderer\core\frame\local_frame_client_impl.cc】
web_frame_->Client()->BeginNavigation(std::move(navigation_info));
5)、在render进程发送BeginNavigation消息给主进程
void RenderFrameImpl::BeginNavigationInternal(
std::unique_ptr<blink::WebNavigationInfo> info,
bool is_history_navigation_in_new_child_frame,
base::TimeTicks renderer_before_unload_start,
base::TimeTicks renderer_before_unload_end){
if (!frame_->WillStartNavigation(*info))
return;
for (auto& observer : observers_)
observer.DidStartNavigation(info->url_request.Url(), info->navigation_type);
is_requesting_navigation_ = true;
// Set SiteForCookies.
WebDocument frame_document = frame_->GetDocument();
if (info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel) {
info->url_request.SetSiteForCookies(
net::SiteForCookies::FromUrl(info->url_request.Url()));
} else {
info->url_request.SetSiteForCookies(frame_document.SiteForCookies());
}
ui::PageTransition transition_type = GetTransitionType(
ui::PAGE_TRANSITION_LINK,
info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
IsMainFrame(), GetWebView()->IsFencedFrameRoot(), info->navigation_type);
if (info->is_client_redirect) {
transition_type = ui::PageTransitionFromInt(
transition_type | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
}
// Note: At this stage, the goal is to apply all the modifications the
// renderer wants to make to the request, and then send it to the browser, so
// that the actual network request can be started. Ideally, all such
// modifications should take place in WillSendRequestInternal, and in the
// implementation of willSendRequest for the various InspectorAgents
// (devtools).
//
// TODO(clamy): Apply devtools override.
// TODO(clamy): Make sure that navigation requests are not modified somewhere
// else in blink.
bool for_outermost_main_frame = frame_->IsOutermostMainFrame();
WillSendRequestInternal(info->url_request, for_outermost_main_frame,
transition_type, ForRedirect(false));
// The extra data was created in WillSendRequestInternal if it didn't exist.
DCHECK(info->url_request.GetURLRequestExtraData());
// These values are assumed on the browser side for navigations. These checks
// ensure the renderer has the correct values.
DCHECK_EQ(network::mojom::RequestMode::kNavigate,
info->url_request.GetMode());
DCHECK_EQ(network::mojom::CredentialsMode::kInclude,
info->url_request.GetCredentialsMode());
DCHECK_EQ(network::mojom::RedirectMode::kManual,
info->url_request.GetRedirectMode());
DCHECK(frame_->Parent() ||
info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel);
DCHECK(!frame_->Parent() ||
info->frame_type == blink::mojom::RequestContextFrameType::kNested);
bool is_form_submission =
(info->navigation_type == blink::kWebNavigationTypeFormSubmitted ||
info->navigation_type ==
blink::kWebNavigationTypeFormResubmittedBackForward ||
info->navigation_type == blink::kWebNavigationTypeFormResubmittedReload);
bool was_initiated_by_link_click =
info->navigation_type == blink::kWebNavigationTypeLinkClicked;
GURL searchable_form_url;
std::string searchable_form_encoding;
if (!info->form.IsNull()) {
WebSearchableFormData web_searchable_form_data(info->form);
searchable_form_url = web_searchable_form_data.Url();
searchable_form_encoding = web_searchable_form_data.Encoding().Utf8();
}
GURL client_side_redirect_url;
if (info->is_client_redirect)
client_side_redirect_url = frame_->GetDocument().Url();
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token(
CloneBlobURLToken(info->blob_url_token));
int load_flags = info->url_request.GetLoadFlagsForWebUrlRequest();
absl::optional<base::Value::Dict> devtools_initiator;
if (!info->devtools_initiator_info.IsNull()) {
absl::optional<base::Value> devtools_initiator_value =
base::JSONReader::Read(info->devtools_initiator_info.Utf8());
if (devtools_initiator_value && devtools_initiator_value->is_dict()) {
devtools_initiator = std::move(*devtools_initiator_value).TakeDict();
}
}
blink::mojom::NavigationInitiatorActivationAndAdStatus
initiator_activation_and_ad_status =
blink::GetNavigationInitiatorActivationAndAdStatus(
info->url_request.HasUserGesture(), info->initiator_frame_is_ad,
info->is_ad_script_in_stack);
blink::mojom::BeginNavigationParamsPtr begin_navigation_params =
blink::mojom::BeginNavigationParams::New(
info->initiator_frame_token,
blink::GetWebURLRequestHeadersAsString(info->url_request).Latin1(),
load_flags, info->url_request.GetSkipServiceWorker(),
blink::GetRequestContextTypeForWebURLRequest(info->url_request),
blink::GetMixedContentContextTypeForWebURLRequest(info->url_request),
is_form_submission, was_initiated_by_link_click,
info->force_history_push, searchable_form_url,
searchable_form_encoding, client_side_redirect_url,
std::move(devtools_initiator),
info->url_request.TrustTokenParams()
? info->url_request.TrustTokenParams()->Clone()
: nullptr,
info->impression, renderer_before_unload_start,
renderer_before_unload_end, initiator_activation_and_ad_status,
info->is_container_initiated, info->is_fullscreen_requested,
info->has_storage_access);
mojo::PendingAssociatedRemote<mojom::NavigationClient>
navigation_client_remote;
BindNavigationClient(
navigation_client_remote.InitWithNewEndpointAndPassReceiver());
mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
renderer_cancellation_listener_receiver;
navigation_client_impl_->SetUpRendererInitiatedNavigation(
renderer_cancellation_listener_receiver.InitWithNewPipeAndPassRemote());
bool current_frame_has_download_sandbox_flag = !frame_->IsAllowedToDownload();
bool has_download_sandbox_flag =
info->initiator_frame_has_download_sandbox_flag ||
current_frame_has_download_sandbox_flag;
bool from_ad = info->initiator_frame_is_ad || frame_->IsAdFrame();
mojo::PendingRemote<blink::mojom::PolicyContainerHostKeepAliveHandle>
initiator_policy_container_keep_alive_handle =
std::move(info->initiator_policy_container_keep_alive_handle);
network::mojom::RequestDestination request_destination =
blink::GetRequestDestinationForWebURLRequest(info->url_request);
//调用FrameHostProxy::BeginNavigation
GetFrameHost()->BeginNavigation(
MakeCommonNavigationParams(frame_->GetSecurityOrigin(), std::move(info),
load_flags, has_download_sandbox_flag, from_ad,
is_history_navigation_in_new_child_frame,
request_destination),
std::move(begin_navigation_params), std::move(blob_url_token),
std::move(navigation_client_remote),
std::move(initiator_policy_container_keep_alive_handle),
std::move(renderer_cancellation_listener_receiver));
}
}
其中GetFrameHost()->BeginNavigation实现在src\out\Debug\gen\content\common\frame.mojom.cc
void FrameHostProxy::BeginNavigation(
::blink::mojom::CommonNavigationParamsPtr in_common_params, ::blink::mojom::BeginNavigationParamsPtr in_begin_params, ::mojo::PendingRemote<::blink::mojom::BlobURLToken> in_blob_url_token, ::mojo::PendingAssociatedRemote<::content::mojom::NavigationClient> in_navigation_client, ::mojo::PendingRemote<::blink::mojom::PolicyContainerHostKeepAliveHandle> in_initiator_policy_container_keep_alive_handle, ::mojo::PendingReceiver<NavigationRendererCancellationListener> in_renderer_cancellation_listener) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
TRACE_EVENT1(
"mojom", "Send content::mojom::FrameHost::BeginNavigation", "input_parameters",
[&](perfetto::TracedValue context){
auto dict = std::move(context).WriteDictionary();
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("common_params"), in_common_params,
"<value of type ::blink::mojom::CommonNavigationParamsPtr>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("begin_params"), in_begin_params,
"<value of type ::blink::mojom::BeginNavigationParamsPtr>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("blob_url_token"), in_blob_url_token,
"<value of type ::mojo::PendingRemote<::blink::mojom::BlobURLToken>>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("navigation_client"), in_navigation_client,
"<value of type ::mojo::PendingAssociatedRemote<::content::mojom::NavigationClient>>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("initiator_policy_container_keep_alive_handle"), in_initiator_policy_container_keep_alive_handle,
"<value of type ::mojo::PendingRemote<::blink::mojom::PolicyContainerHostKeepAliveHandle>>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("renderer_cancellation_listener"), in_renderer_cancellation_listener,
"<value of type ::mojo::PendingReceiver<NavigationRendererCancellationListener>>");
});
#endif
const bool kExpectsResponse = false;
const bool kIsSync = false;
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_BeginNavigation_Name, kFlags, 0, 0, nullptr);
mojo::internal::MessageFragment<
::content::mojom::internal::FrameHost_BeginNavigation_Params_Data> params(
message);
params.Allocate();
mojo::internal::MessageFragment<
typename decltype(params->common_params)::BaseType> common_params_fragment(
params.message());
mojo::internal::Serialize<::blink::mojom::CommonNavigationParamsDataView>(
in_common_params, common_params_fragment);
params->common_params.Set(
common_params_fragment.is_null() ? nullptr : common_params_fragment.data());
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
params->common_params.is_null(),
mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
"null common_params in FrameHost.BeginNavigation request");
mojo::internal::MessageFragment<
typename decltype(params->begin_params)::BaseType> begin_params_fragment(
params.message());
mojo::internal::Serialize<::blink::mojom::BeginNavigationParamsDataView>(
in_begin_params, begin_params_fragment);
params->begin_params.Set(
begin_params_fragment.is_null() ? nullptr : begin_params_fragment.data());
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
params->begin_params.is_null(),
mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
"null begin_params in FrameHost.BeginNavigation request");
mojo::internal::Serialize<mojo::InterfacePtrDataView<::blink::mojom::BlobURLTokenInterfaceBase>>(
in_blob_url_token, ¶ms->blob_url_token, ¶ms.message());
mojo::internal::Serialize<::content::mojom::NavigationClientAssociatedPtrInfoDataView>(
in_navigation_client, ¶ms->navigation_client, ¶ms.message());
mojo::internal::Serialize<mojo::InterfacePtrDataView<::blink::mojom::PolicyContainerHostKeepAliveHandleInterfaceBase>>(
in_initiator_policy_container_keep_alive_handle, ¶ms->initiator_policy_container_keep_alive_handle, ¶ms.message());
mojo::internal::Serialize<mojo::InterfaceRequestDataView<::content::mojom::NavigationRendererCancellationListenerInterfaceBase>>(
in_renderer_cancellation_listener, ¶ms->renderer_cancellation_listener, ¶ms.message());
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
!mojo::internal::IsHandleOrInterfaceValid(params->renderer_cancellation_listener),
mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
"invalid renderer_cancellation_listener in FrameHost.BeginNavigation request");
#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name(FrameHost::Name_);
message.set_method_name("BeginNavigation");
#endif
// This return value may be ignored as false implies the Connector has
// encountered an error, which will be visible through other means.
::mojo::internal::SendMojoMessage(*receiver_, message);
}
子进程发送BeginNavigation堆栈截图:
6)、主进程接收到void RenderFrameHostImpl::BeginNavigation()打开链接响应。进而打开<a ref="index.html" />
content\browser\renderer_host\render_frame_host_impl.cc
void RenderFrameHostImpl::BeginNavigation(
blink::mojom::CommonNavigationParamsPtr unvalidated_common_params,
blink::mojom::BeginNavigationParamsPtr begin_params,
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token,
mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
mojo::PendingRemote<blink::mojom::PolicyContainerHostKeepAliveHandle>
initiator_policy_container_host_keep_alive_handle,
mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
renderer_cancellation_listener) {
TRACE_EVENT("navigation", "RenderFrameHostImpl::BeginNavigation",
ChromeTrackEvent::kRenderFrameHost, this, "url",
unvalidated_common_params->url.possibly_invalid_spec());
// Only active and prerendered documents are allowed to start navigation in
// their frame.
if (lifecycle_state() != LifecycleStateImpl::kPrerendering) {
// If this is reached in case the RenderFrameHost is in BackForwardCache
// evict the document from BackForwardCache.
if (IsInactiveAndDisallowActivation(
DisallowActivationReasonId::kBeginNavigation)) {
return;
}
}
// See `owner_` invariants about `lifecycle_state_`.
// `IsInactiveAndDisallowActivation()` check cause both pending deletion and
// bfcached states to return early.
DCHECK(owner_);
if (owner_->GetRenderFrameHostManager().is_attaching_inner_delegate()) {
// Avoid starting any new navigations since this frame is in the process of
// attaching an inner delegate.
return;
}
DCHECK(navigation_client.is_valid());
blink::mojom::CommonNavigationParamsPtr validated_common_params =
unvalidated_common_params.Clone();
if (!VerifyBeginNavigationCommonParams(*this, &*validated_common_params,
begin_params->initiator_frame_token)) {
return;
}
// BeginNavigation() should only be triggered when the navigation is
// initiated by a document in the same process.
int initiator_process_id = GetProcess()->GetID();
if (!VerifyNavigationInitiator(this, begin_params->initiator_frame_token,
initiator_process_id)) {
return;
}
// Container-initiated navigations must come from the same process as the
// parent.
if (begin_params->is_container_initiated) {
if (!GetParent() ||
(initiator_process_id != GetParent()->GetProcess()->GetID())) {
mojo::ReportBadMessage(
"container initiated navigation from non-parent process");
return;
}
}
// If the request is bearing Private State Tokens parameters:
// - it must not be a main-frame navigation, and
// - for redemption and signing operations, the frame's parent needs the
// private-state-token-redemption Permissions Policy feature,
// - for issue operation, the frame's parent needs the
// private-state-token-issuance Permission Policy.
if (begin_params->trust_token_params) {
// For Fenced Frame trust_token_params shouldn't be populated since that is
// driven by the iframe specific attribute as defined here:
// https://github.com/WICG/trust-token-api#extension-iframe-activation". If
// this changes, the code below using `GetParent()` will need to be changed.
if (IsFencedFrameRoot()) {
mojo::ReportBadMessage(
"RFHI: Private State Token params in fenced frame nav");
return;
}
if (!GetParent()) {
mojo::ReportBadMessage(
"RFHI: Private State Token params in main frame nav");
return;
}
RenderFrameHostImpl* parent = GetParent();
bool is_right_operation_policy_enabled = false;
const network::mojom::TrustTokenOperationType& operation =
begin_params->trust_token_params->operation;
switch (operation) {
case network::mojom::TrustTokenOperationType::kRedemption:
case network::mojom::TrustTokenOperationType::kSigning:
is_right_operation_policy_enabled = parent->IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kTrustTokenRedemption);
break;
case network::mojom::TrustTokenOperationType::kIssuance:
is_right_operation_policy_enabled = parent->IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kPrivateStateTokenIssuance);
break;
}
if (!is_right_operation_policy_enabled) {
mojo::ReportBadMessage(
"RFHI: Mandatory Private State Tokens Permissions Policy feature "
"is absent");
return;
}
}
if (begin_params->is_fullscreen_requested) {
// Fullscreen requests on navigation are only allowed from initial empty
// documents that are the outermost main frame.
if (!is_initial_empty_document()) {
bad_message::ReceivedBadMessage(
GetProcess(),
bad_message::RFHI_FULLSCREEN_NAV_INVALID_INITIAL_DOCUMENT);
return;
}
if (!IsOutermostMainFrame()) {
bad_message::ReceivedBadMessage(
GetProcess(),
bad_message::RFHI_FULLSCREEN_NAV_NOT_OUTERMOST_MAIN_FRAME);
return;
}
RenderFrameHostImpl* initiator_render_frame_host =
begin_params->initiator_frame_token
? RenderFrameHostImpl::FromFrameToken(
initiator_process_id,
begin_params->initiator_frame_token.value())
: nullptr;
// The initiator must have window-management permission, permission
// policy and `fullscreen` permission policy granted and the navigation must
// be from a user gesture, otherwise the fullscreen bit is dropped.
if (!initiator_render_frame_host ||
!validated_common_params->has_user_gesture ||
!IsWindowManagementGranted(initiator_render_frame_host) ||
!initiator_render_frame_host->permissions_policy()->IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kFullscreen) ||
!initiator_render_frame_host->permissions_policy()->IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kWindowManagement)) {
if (initiator_render_frame_host) {
initiator_render_frame_host->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning,
"Fullscreen request ignored: Insufficient permissions "
"or user activation.");
}
begin_params->is_fullscreen_requested = false;
}
}
GetProcess()->FilterURL(true, &begin_params->searchable_form_url);
// If the request was for a blob URL, but the validated URL is no longer a
// blob URL, reset the blob_url_token to prevent hitting the ReportBadMessage
// below, and to make sure we don't incorrectly try to use the blob_url_token.
if (unvalidated_common_params->url.SchemeIsBlob() &&
!validated_common_params->url.SchemeIsBlob()) {
blob_url_token = mojo::NullRemote();
}
if (blob_url_token && !validated_common_params->url.SchemeIsBlob()) {
mojo::ReportBadMessage("Blob URL Token, but not a blob: URL");
return;
}
scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
if (blob_url_token) {
blob_url_loader_factory =
ChromeBlobStorageContext::URLLoaderFactoryForToken(
GetStoragePartition(), std::move(blob_url_token));
}
// If the request was for a blob URL, but no blob_url_token was passed in this
// is not necessarily an error. Renderer initiated reloads for example are one
// situation where a renderer currently doesn't have an easy way of resolving
// the blob URL. For those situations resolve the blob URL here, as we don't
// care about ordering with other blob URL manipulation anyway.
if (validated_common_params->url.SchemeIsBlob() && !blob_url_loader_factory) {
blob_url_loader_factory = ChromeBlobStorageContext::URLLoaderFactoryForUrl(
GetStoragePartition(), validated_common_params->url);
}
if (waiting_for_init_) {
pending_navigate_ = std::make_unique<PendingNavigation>(
std::move(validated_common_params), std::move(begin_params),
std::move(blob_url_loader_factory), std::move(navigation_client),
std::move(renderer_cancellation_listener));
return;
}
if (begin_params->initiator_frame_token) {
RenderFrameHostImpl* initiator_frame = RenderFrameHostImpl::FromFrameToken(
GetProcess()->GetID(), begin_params->initiator_frame_token.value());
if (IsOutermostMainFrame()) {
MaybeRecordAdClickMainFrameNavigationUseCounter(
initiator_frame, begin_params->initiator_activation_and_ad_status);
}
}
// We can discard the parameter
// |initiator_policy_container_host_keep_alive_handle|. This is just needed to
// ensure that the PolicyContainerHost of the initiator RenderFrameHost
// can still be retrieved even if the RenderFrameHost has been deleted in
// between. Since the NavigationRequest will be created synchronously as a
// result of this function's execution, we don't need to pass
// |initiator_policy_container_host_keep_alive_handle| along.
owner_->GetCurrentNavigator().OnBeginNavigation(
frame_tree_node(), std::move(validated_common_params),
std::move(begin_params), std::move(blob_url_loader_factory),
std::move(navigation_client), EnsurePrefetchedSignedExchangeCache(),
initiator_process_id, std::move(renderer_cancellation_listener));
}
主进程RenderFrameHostImpl::BeginNavigation 堆栈截图【content\browser\renderer_host\render_frame_host_impl.cc】:
至此分析完毕。
总结:
html_anchor_element html链接类,在render进程鼠标点击链接时候查找 kHrefAttr属性,最终调用
content\common\frame.mojom中定义的
BeginNavigation(
blink.mojom.CommonNavigationParams common_params,
blink.mojom.BeginNavigationParams begin_params,
pending_remote<blink.mojom.BlobURLToken>? blob_url_token,
pending_associated_remote<NavigationClient>? navigation_client,
pending_remote<blink.mojom.PolicyContainerHostKeepAliveHandle>?
initiator_policy_container_keep_alive_handle,
pending_receiver<NavigationRendererCancellationListener>
renderer_cancellation_listener);
接口给主进程RenderFrameHostImpl::BeginNavigation响应此函数处理。