Chromium 中<a> 标签href 属性实现分析c++

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, &params->blob_url_token, &params.message());
  mojo::internal::Serialize<::content::mojom::NavigationClientAssociatedPtrInfoDataView>(
      in_navigation_client, &params->navigation_client, &params.message());
  mojo::internal::Serialize<mojo::InterfacePtrDataView<::blink::mojom::PolicyContainerHostKeepAliveHandleInterfaceBase>>(
      in_initiator_policy_container_keep_alive_handle, &params->initiator_policy_container_keep_alive_handle, &params.message());
  mojo::internal::Serialize<mojo::InterfaceRequestDataView<::content::mojom::NavigationRendererCancellationListenerInterfaceBase>>(
      in_renderer_cancellation_listener, &params->renderer_cancellation_listener, &params.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响应此函数处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值