blob: abf3d1d02c34edf99cff8bd33aadbe80bbe99240 [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 "ash/assistant/assistant_response_processor.h"
#include "ash/assistant/assistant_controller.h"
#include "ash/assistant/model/assistant_response.h"
#include "ash/assistant/model/assistant_ui_element.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "base/base64.h"
namespace ash {
namespace {
// WebContents.
constexpr char kDataUriPrefix[] = "data:text/html;base64,";
} // namespace
// AssistantResponseProcessor::Task --------------------------------------------
AssistantResponseProcessor::Task::Task(AssistantResponse& response,
ProcessCallback callback)
: response(response.GetWeakPtr()), callback(std::move(callback)) {}
AssistantResponseProcessor::Task::~Task() = default;
// AssistantResponseProcessor --------------------------------------------------
AssistantResponseProcessor::AssistantResponseProcessor(
AssistantController* assistant_controller)
: assistant_controller_(assistant_controller), weak_factory_(this) {}
AssistantResponseProcessor::~AssistantResponseProcessor() = default;
void AssistantResponseProcessor::Process(AssistantResponse& response,
ProcessCallback callback) {
// We should only attempt to process responses that are unprocessed.
DCHECK_EQ(AssistantResponse::ProcessingState::kUnprocessed,
response.processing_state());
// Update processing state.
response.set_processing_state(
AssistantResponse::ProcessingState::kProcessing);
// We only support processing a single task at a time. As such, we should
// abort any task in progress before creating and starting a new one.
TryAbortingTask();
// Create a task.
task_.emplace(/*response=*/response,
/*callback=*/std::move(callback));
// Start processing UI elements.
for (const auto& ui_element : response.GetUiElements()) {
switch (ui_element->GetType()) {
case AssistantUiElementType::kCard:
ProcessCardElement(
static_cast<AssistantCardElement*>(ui_element.get()));
break;
case AssistantUiElementType::kText:
// No processing necessary.
break;
}
}
// Try finishing. This will no-op if there are still UI elements being
// processed asynchronously.
TryFinishingTask();
}
void AssistantResponseProcessor::ProcessCardElement(
AssistantCardElement* card_element) {
// Encode the card HTML using base64.
std::string encoded_html;
base::Base64Encode(card_element->html(), &encoded_html);
// TODO(dmblack): Find a better way of determining desired card size.
const int width_dip = kPreferredWidthDip - 2 * kUiElementHorizontalMarginDip;
// Configure parameters for the card.
ash::mojom::ManagedWebContentsParamsPtr params(
ash::mojom::ManagedWebContentsParams::New());
params->url = GURL(kDataUriPrefix + encoded_html);
params->min_size_dip = gfx::Size(width_dip, 0);
params->max_size_dip = gfx::Size(width_dip, INT_MAX);
// Request an embed token for the card whose WebContents will be owned by
// WebContentsManager.
assistant_controller_->ManageWebContents(
card_element->id_token(), std::move(params),
base::BindOnce(&AssistantResponseProcessor::OnCardElementProcessed,
weak_factory_.GetWeakPtr(), card_element));
// Increment |processing_count| to reflect the fact that a card element is
// being processed asynchronously.
++task_->processing_count;
}
void AssistantResponseProcessor::OnCardElementProcessed(
AssistantCardElement* card_element,
const base::Optional<base::UnguessableToken>& embed_token) {
// If the response has been invalidated we should abort early.
if (!task_->response) {
TryAbortingTask();
return;
}
// Save the |embed_token|.
card_element->set_embed_token(embed_token);
// Decrement |processing_count| to reflect the fact that a card element has
// finished being processed asynchronously.
--task_->processing_count;
// Try finishing. This will no-op if there are still UI elements being
// processed asynchronously.
TryFinishingTask();
}
void AssistantResponseProcessor::TryAbortingTask() {
if (!task_)
return;
// Invalidate weak pointers to prevent processing callbacks from running.
// Otherwise we might continue receiving card events for the aborted task.
weak_factory_.InvalidateWeakPtrs();
// Notify our callback and clean up any task related resources.
std::move(task_->callback).Run(/*success=*/false);
task_.reset();
}
void AssistantResponseProcessor::TryFinishingTask() {
// This method is a no-op if we are still processing.
if (task_->processing_count > 0)
return;
// Update processing state.
task_->response->set_processing_state(
AssistantResponse::ProcessingState::kProcessed);
// Notify our callback and clean up any task related resources.
std::move(task_->callback).Run(/*success=*/true);
task_.reset();
}
} // namespace ash