blob: 80fb40f2229fd830b365fa21ba13e9a15036243e [file]
// 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 "pdf/pdf_annotation_agent.h"
#include <utility>
#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "base/state_transitions.h"
namespace chrome_pdf {
PdfAnnotationAgent::PdfAnnotationAgent(
Container* container,
blink::mojom::AnnotationType type,
blink::mojom::SelectorPtr selector,
mojo::PendingRemote<blink::mojom::AnnotationAgentHost> host_remote,
mojo::PendingReceiver<blink::mojom::AnnotationAgent> agent_receiver)
: container_(container) {
agent_host_.Bind(std::move(host_remote));
receiver_.Bind(std::move(agent_receiver));
receiver_.set_disconnect_handler(base::BindOnce(
&PdfAnnotationAgent::RemoveTextFragments, weak_factory_.GetWeakPtr()));
auto attachment_result = blink::mojom::AttachmentResult::kSelectorNotMatched;
if (type == blink::mojom::AnnotationType::kGlic &&
selector->which() == blink::mojom::Selector::Tag::kSerializedSelector) {
const std::string& serialized = selector->get_serialized_selector();
if (!serialized.empty() && container_->FindAndHighlightTextFragments(
base::span_from_ref(serialized))) {
attachment_result = blink::mojom::AttachmentResult::kSuccess;
}
}
agent_host_->DidFinishAttachment(gfx::Rect(), attachment_result);
SetState(attachment_result == blink::mojom::AttachmentResult::kSuccess
? State::kActive
: State::kFailure);
}
PdfAnnotationAgent::~PdfAnnotationAgent() {
RemoveTextFragments();
}
void PdfAnnotationAgent::ScrollIntoView(bool applies_focus) {
// The text fragment results can be invalidated between DidFinishAttachment()
// and ScrollIntoView(). Do not attempt to scroll if the results are
// invalidated.
if (state_ == State::kHighlightDropped) {
return;
}
// ScrollIntoView() is only valid after the construction, shouldn't be called
// if the text fragment isn't found.
CHECK_EQ(state_, State::kActive);
container_->ScrollTextFragmentIntoView();
}
void PdfAnnotationAgent::RemoveTextFragments() {
if (state_ == State::kFailure || state_ == State::kHighlightDropped) {
return;
}
// Reset() shouldn't be called while in the kInitial state as the search
// completes synchronously in the constructor.
CHECK_EQ(state_, State::kActive);
container_->RemoveTextFragments();
SetState(State::kHighlightDropped);
}
void PdfAnnotationAgent::SetState(State new_state) {
static const base::NoDestructor<base::StateTransitions<State>>
allowed_transitions(base::StateTransitions<State>(
{{State::kInitial, {State::kActive, State::kFailure}},
{State::kActive, {State::kHighlightDropped}},
{State::kHighlightDropped, {}},
{State::kFailure, {}}}));
CHECK_STATE_TRANSITION(allowed_transitions, state_, new_state);
state_ = new_state;
}
} // namespace chrome_pdf