blob: 1deb53f3aa16a9bba2133622e5d6dc2588254551 [file]
// Copyright 2022 The Chromium Authors
// 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/annotation/annotation_agent_generator.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/visible_selection.h"
#include "third_party/blink/renderer/core/fragment_directive/text_fragment_selector.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
namespace blink {
AnnotationAgentGenerator::AnnotationAgentGenerator(LocalFrame* frame)
: frame_(frame) {}
void AnnotationAgentGenerator::Trace(Visitor* visitor) const {
visitor->Trace(generator_);
visitor->Trace(frame_);
}
void AnnotationAgentGenerator::GetForCurrentSelection(
mojom::blink::AnnotationType type,
SelectorGenerationCallback callback) {
// A valid callback, means there's an ongoing previous request. In that case,
// the previous request is canceled with an error for the new one.
// TODO(crbug.com/1313967): Find right behavior from a product perspective.
if (callback_) {
std::move(callback_).Run(
type_,
shared_highlighting::LinkGenerationReadyStatus::kRequestedAfterReady,
"", TextFragmentSelector(TextFragmentSelector::SelectorType::kInvalid),
shared_highlighting::LinkGenerationError::kUnknown);
}
callback_ = std::move(callback);
type_ = type;
// Preemptive generation was completed.
if (generation_result_.has_value()) {
InvokeCompletionCallbackIfNeeded(
shared_highlighting::LinkGenerationReadyStatus::kRequestedAfterReady);
return;
}
// If a generator already exists, it is because a search is in progress so
// we'll return the result when it finishes.
if (generator_)
return;
GenerateSelector();
}
void AnnotationAgentGenerator::InvokeCompletionCallbackIfNeeded(
shared_highlighting::LinkGenerationReadyStatus ready_status) {
DCHECK(callback_);
if (selector_error_ == shared_highlighting::LinkGenerationError::kNone) {
DCHECK(generation_result_.has_value());
DCHECK(generator_);
}
std::move(callback_).Run(
type_, ready_status,
generator_ ? generator_->GetSelectorTargetText() : "",
generation_result_.value(), selector_error_);
if (generator_) {
generator_->Reset();
}
generation_result_.reset();
selector_error_ = shared_highlighting::LinkGenerationError::kNone;
}
void AnnotationAgentGenerator::PreemptivelyGenerateForCurrentSelection() {
// A valid callback means that a generation started and the callback is
// waiting on the result.
if (callback_) {
DCHECK(generator_);
return;
}
// Reset generation_result if it has a value and no callback. This means that
// preemptive link generation was triggered previously but the result was
// never used.
if (generation_result_.has_value()) {
generation_result_.reset();
}
GenerateSelector();
}
void AnnotationAgentGenerator::GenerateSelector() {
DCHECK(!generation_result_);
selector_error_ = shared_highlighting::LinkGenerationError::kNone;
VisibleSelectionInFlatTree selection =
frame_->Selection().ComputeVisibleSelectionInFlatTree();
if (selection.IsNone() || !selection.IsRange()) {
if (callback_) {
generation_result_.emplace(
TextFragmentSelector(TextFragmentSelector::SelectorType::kInvalid));
selector_error_ =
shared_highlighting::LinkGenerationError::kEmptySelection;
InvokeCompletionCallbackIfNeeded(
shared_highlighting::LinkGenerationReadyStatus::
kRequestedBeforeReady);
}
return;
}
EphemeralRangeInFlatTree selection_range(selection.Start(), selection.End());
if (selection_range.IsNull() || selection_range.IsCollapsed()) {
if (callback_) {
generation_result_.emplace(
TextFragmentSelector(TextFragmentSelector::SelectorType::kInvalid));
selector_error_ = shared_highlighting::LinkGenerationError::kNoRange;
InvokeCompletionCallbackIfNeeded(
shared_highlighting::LinkGenerationReadyStatus::
kRequestedBeforeReady);
}
return;
}
RangeInFlatTree* current_selection_range =
MakeGarbageCollected<RangeInFlatTree>(selection_range.StartPosition(),
selection_range.EndPosition());
// Make sure the generator is valid before starting the generation.
if (!generator_) {
generator_ = MakeGarbageCollected<TextFragmentSelectorGenerator>(frame_);
}
generator_->Generate(*current_selection_range,
BindOnce(&AnnotationAgentGenerator::DidFinishGeneration,
WrapWeakPersistent(this)));
}
void AnnotationAgentGenerator::DidFinishGeneration(
const TextFragmentSelector& selector,
shared_highlighting::LinkGenerationError error) {
DCHECK(!generation_result_.has_value());
generation_result_.emplace(selector);
selector_error_ = error;
if (callback_) {
generation_result_.emplace(selector);
selector_error_ = error;
InvokeCompletionCallbackIfNeeded(
shared_highlighting::LinkGenerationReadyStatus::kRequestedAfterReady);
}
}
} // namespace blink