blob: eb59cf3c6edc3bb4375ece3451b333d3cd63a0ad [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/picker/picker_insert_media.h"
#include <utility>
#include "ash/picker/picker_rich_media.h"
#include "base/base64.h"
#include "base/files/file_util.h"
#include "base/functional/callback.h"
#include "base/functional/overloaded.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "net/base/mime_util.h"
#include "ui/base/ime/text_input_client.h"
#include "url/gurl.h"
namespace ash {
namespace {
std::optional<std::string> GetMediaTypeFromFilePath(
const base::FilePath& path) {
std::string mime_type;
if (!net::GetMimeTypeFromFile(path, &mime_type)) {
return std::nullopt;
}
return mime_type;
}
std::optional<std::string> ReadFileToString(const base::FilePath& path) {
std::string result;
if (!base::ReadFileToString(path, &result)) {
LOG(WARNING) << "Failed reading file";
return std::nullopt;
}
return result;
}
void ReadFileAsync(
base::FilePath path,
base::OnceCallback<void(std::optional<std::string>)> callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&ReadFileToString, std::move(path)), std::move(callback));
}
std::optional<GURL> ConvertToDataUrl(std::string_view media_type,
std::optional<std::string> data) {
if (!data.has_value()) {
return std::nullopt;
}
return GURL(base::StrCat(
{"data:", media_type, ";base64,", base::Base64Encode(*data)}));
}
} // namespace
bool InputFieldSupportsInsertingMedia(const PickerRichMedia& media,
ui::TextInputClient& client) {
return std::visit(base::Overloaded{
[](const PickerTextMedia& media) { return true; },
[&client](const PickerImageMedia& media) {
return client.CanInsertImage();
},
[](const PickerLinkMedia& media) { return true; },
[&client](const PickerLocalFileMedia& media) {
return client.CanInsertImage();
},
},
media);
}
void InsertMediaToInputField(PickerRichMedia media,
ui::TextInputClient& client,
OnInsertMediaCompleteCallback callback) {
std::visit(
base::Overloaded{
[&client, &callback](PickerTextMedia media) mutable {
client.InsertText(media.text,
ui::TextInputClient::InsertTextCursorBehavior::
kMoveCursorAfterText);
std::move(callback).Run(InsertMediaResult::kSuccess);
},
[&client, &callback](PickerImageMedia media) mutable {
if (!client.CanInsertImage()) {
std::move(callback).Run(InsertMediaResult::kUnsupported);
return;
}
client.InsertImage(media.url);
std::move(callback).Run(InsertMediaResult::kSuccess);
},
[&client, &callback](PickerLinkMedia media) mutable {
// TODO(b/322729192): Insert a real hyperlink.
client.InsertText(base::UTF8ToUTF16(media.url.spec()),
ui::TextInputClient::InsertTextCursorBehavior::
kMoveCursorAfterText);
std::move(callback).Run(InsertMediaResult::kSuccess);
},
[&client, &callback](PickerLocalFileMedia media) mutable {
if (!client.CanInsertImage()) {
std::move(callback).Run(InsertMediaResult::kUnsupported);
return;
}
std::optional<std::string> media_type =
GetMediaTypeFromFilePath(media.path);
if (!media_type.has_value()) {
std::move(callback).Run(InsertMediaResult::kUnsupported);
return;
}
ReadFileAsync(
media.path,
base::BindOnce(ConvertToDataUrl, std::move(*media_type))
.Then(base::BindOnce(
[](base::WeakPtr<ui::TextInputClient> client,
OnInsertMediaCompleteCallback callback,
std::optional<GURL> url) {
if (!url.has_value()) {
std::move(callback).Run(
InsertMediaResult::kNotFound);
return;
}
client->InsertImage(*url);
std::move(callback).Run(InsertMediaResult::kSuccess);
},
client.AsWeakPtr(), std::move(callback))));
},
},
std::move(media));
}
} // namespace ash