blob: 994138319c6850067c1060244e561570a8fe0b6b [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/actor/ui/dom_node_geometry.h"
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "chrome/browser/actor/ui/actor_ui_metrics.h"
#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
namespace actor::ui {
namespace {
using optimization_guide::proto::AnnotatedPageContent;
using optimization_guide::proto::ContentAttributes;
using optimization_guide::proto::ContentNode;
using optimization_guide::proto::DocumentIdentifier;
using NodeGeomMap = DomNodeGeometry::NodeGeomMap;
void BuildNodeMapInternal(const DocumentIdentifier& doc_id,
const ContentNode& root,
NodeGeomMap& map) {
const auto& content_attributes = root.content_attributes();
if (content_attributes.has_common_ancestor_dom_node_id()) {
DomNode key{.node_id = content_attributes.common_ancestor_dom_node_id(),
.document_identifier = doc_id.serialized_token()};
map.emplace(std::move(key), root.content_attributes());
}
// If the node is an iframe, then use the iframe's doc_id for its children.
// Otherwise use the same doc_id.
const DocumentIdentifier& child_doc_id =
(content_attributes.has_iframe_data() &&
content_attributes.iframe_data().has_frame_data())
? content_attributes.iframe_data().frame_data().document_identifier()
: doc_id;
for (const auto& child : root.children_nodes()) {
BuildNodeMapInternal(child_doc_id, child, map);
}
}
NodeGeomMap BuildNodeMap(const DocumentIdentifier& doc_id,
const ContentNode& root) {
NodeGeomMap map;
BuildNodeMapInternal(doc_id, root, map);
return map;
}
} // namespace
std::unique_ptr<DomNodeGeometry> DomNodeGeometry::InitFromApc(
const AnnotatedPageContent& apc) {
TRACE_EVENT("actor", "DomNodeGeometry::InitFromApc");
if (!apc.has_main_frame_data() ||
!apc.main_frame_data().has_document_identifier()) {
return base::WrapUnique(
new DomNodeGeometry(GetDomNodeResult::kNoApcMainFrameData));
}
auto map = BuildNodeMap(apc.main_frame_data().document_identifier(),
apc.root_node());
return base::WrapUnique(new DomNodeGeometry(std::move(map)));
}
base::expected<gfx::Point, GetDomNodeResult> DomNodeGeometry::GetDomNode(
const DomNode& node) const {
TRACE_EVENT("actor", "DomNodeGeometry::GetDomNode");
auto result = InternalGetDomNode(node);
RecordGetDomNodeResult(result.error_or(GetDomNodeResult::kSuccess));
return result;
}
base::expected<gfx::Point, GetDomNodeResult>
DomNodeGeometry::InternalGetDomNode(const DomNode& node) const {
if (init_error_.has_value()) {
return base::unexpected(init_error_.value());
}
auto it = node_map_.find(node);
if (it == node_map_.end()) {
return base::unexpected(GetDomNodeResult::kNodeNotFoundInApc);
}
const ContentAttributes& attr = it->second;
if (!attr.has_geometry()) {
return base::unexpected(GetDomNodeResult::kNoGeometry);
}
const auto& geom = attr.geometry();
if (!geom.has_visible_bounding_box()) {
return base::unexpected(GetDomNodeResult::kOffScreen);
}
const auto& rect = geom.visible_bounding_box();
const int x = rect.x() + rect.width() / 2;
const int y = rect.y() + rect.height() / 2;
return gfx::Point(x, y);
}
DomNodeGeometry::DomNodeGeometry(GetDomNodeResult init_error)
: init_error_(init_error), node_map_() {}
DomNodeGeometry::DomNodeGeometry(NodeGeomMap node_map)
: init_error_(std::nullopt), node_map_(std::move(node_map)) {}
DomNodeGeometry::~DomNodeGeometry() = default;
} // namespace actor::ui