blob: 99a858f595f29c0242f3f3d56ae313bcf004f4e0 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/html/anchor_element_metrics_sender.h"
#include "base/metrics/histogram_macros.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/anchor_element_metrics.h"
#include "third_party/blink/renderer/core/html/html_anchor_element.h"
#include "ui/gfx/geometry/mojom/geometry.mojom-shared.h"
namespace blink {
namespace {
// Returns true if |anchor_element| should be discarded, and not used for
// navigation prediction.
bool ShouldDiscardAnchorElement(const HTMLAnchorElement& anchor_element) {
Frame* frame = anchor_element.GetDocument().GetFrame();
auto* local_frame = DynamicTo<LocalFrame>(frame);
if (!local_frame)
return true;
return local_frame->IsAdSubframe();
}
} // namespace
// static
const char AnchorElementMetricsSender::kSupplementName[] =
"DocumentAnchorElementMetricsSender";
AnchorElementMetricsSender::~AnchorElementMetricsSender() = default;
// static
AnchorElementMetricsSender* AnchorElementMetricsSender::From(
Document& document) {
DCHECK(HasAnchorElementMetricsSender(document));
AnchorElementMetricsSender* sender =
Supplement<Document>::From<AnchorElementMetricsSender>(document);
if (!sender) {
sender = MakeGarbageCollected<AnchorElementMetricsSender>(document);
ProvideTo(document, sender);
}
return sender;
}
// static
bool AnchorElementMetricsSender::HasAnchorElementMetricsSender(
Document& document) {
bool is_feature_enabled =
base::FeatureList::IsEnabled(features::kNavigationPredictor);
const KURL& url = document.BaseURL();
return is_feature_enabled && !document.ParentDocument() && url.IsValid() &&
url.ProtocolIsInHTTPFamily();
}
void AnchorElementMetricsSender::SendClickedAnchorMetricsToBrowser(
mojom::blink::AnchorElementMetricsPtr metric) {
if (!AssociateInterface())
return;
metrics_host_->ReportAnchorElementMetricsOnClick(std::move(metric));
}
void AnchorElementMetricsSender::SendAnchorMetricsVectorToBrowser(
Vector<mojom::blink::AnchorElementMetricsPtr> metrics,
const IntSize& viewport_size) {
if (!AssociateInterface())
return;
metrics_host_->ReportAnchorElementMetricsOnLoad(std::move(metrics),
gfx::Size(viewport_size));
has_onload_report_sent_ = true;
anchor_elements_.clear();
}
void AnchorElementMetricsSender::AddAnchorElement(HTMLAnchorElement& element) {
if (has_onload_report_sent_)
return;
bool is_ad_frame_element = ShouldDiscardAnchorElement(element);
// We ignore anchor elements that are in ad frames.
if (is_ad_frame_element)
return;
anchor_elements_.insert(&element);
}
const HeapHashSet<Member<HTMLAnchorElement>>&
AnchorElementMetricsSender::GetAnchorElements() const {
return anchor_elements_;
}
void AnchorElementMetricsSender::Trace(Visitor* visitor) const {
visitor->Trace(anchor_elements_);
visitor->Trace(metrics_host_);
Supplement<Document>::Trace(visitor);
}
bool AnchorElementMetricsSender::AssociateInterface() {
if (metrics_host_.is_bound())
return true;
Document* document = GetSupplementable();
// Unable to associate since no frame is attached.
if (!document->GetFrame())
return false;
document->GetFrame()->GetBrowserInterfaceBroker().GetInterface(
metrics_host_.BindNewPipeAndPassReceiver(
document->GetExecutionContext()->GetTaskRunner(
TaskType::kInternalDefault)));
return true;
}
AnchorElementMetricsSender::AnchorElementMetricsSender(Document& document)
: Supplement<Document>(document),
metrics_host_(document.GetExecutionContext()) {
DCHECK(!document.ParentDocument());
}
void AnchorElementMetricsSender::DidFinishLifecycleUpdate(
const LocalFrameView& local_frame_view) {
// Check that layout is stable. If it is, we can perform the onload update and
// stop observing future events.
Document* document = local_frame_view.GetFrame().GetDocument();
if (document->Lifecycle().GetState() <
DocumentLifecycle::kAfterPerformLayout) {
return;
}
// Stop listening to updates, as the onload report can be sent now.
document->View()->UnregisterFromLifecycleNotifications(this);
// Send onload report.
AnchorElementMetrics::MaybeReportViewportMetricsOnLoad(*document);
}
} // namespace blink