blob: a3886d65480a23197a6fd90c13fb71772a780ee8 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/ui/web_applications/share_target_utils.h"
#include "base/memory/scoped_refptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/web_share_target/target_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "components/services/app_service/public/cpp/share_target.h"
#include "extensions/common/constants.h"
#include "net/base/mime_util.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/arc/nearby_share/share_info_file_handler.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/web_applications/file_stream_data_pipe_getter.h"
#endif
namespace web_app {
bool SharedField::operator==(const SharedField& other) const {
return name == other.name && value == other.value;
}
std::vector<SharedField> ExtractSharedFields(
const apps::ShareTarget& share_target,
const apps::mojom::Intent& intent) {
std::vector<SharedField> result;
if (!share_target.params.title.empty() && intent.share_title.has_value() &&
!intent.share_title->empty()) {
result.push_back(
{.name = share_target.params.title, .value = *intent.share_title});
}
if (!intent.share_text.has_value())
return result;
apps_util::SharedText extracted_text =
apps_util::ExtractSharedText(*intent.share_text);
if (!share_target.params.text.empty() && !extracted_text.text.empty())
result.push_back(
{.name = share_target.params.text, .value = extracted_text.text});
if (!share_target.params.url.empty() && !extracted_text.url.is_empty())
result.push_back(
{.name = share_target.params.url, .value = extracted_text.url.spec()});
return result;
}
NavigateParams NavigateParamsForShareTarget(
Browser* browser,
const apps::ShareTarget& share_target,
const apps::mojom::Intent& intent,
const std::vector<base::FilePath>& launch_files) {
NavigateParams nav_params(browser, share_target.action,
ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
#if BUILDFLAG(IS_CHROMEOS)
std::vector<std::string> names;
std::vector<std::string> values;
std::vector<bool> is_value_file_uris;
std::vector<std::string> filenames;
std::vector<std::string> types;
std::vector<mojo::PendingRemote<network::mojom::DataPipeGetter>>
data_pipe_getters;
if (intent.mime_type.has_value() && intent.files.has_value()) {
if (!launch_files.empty()) {
DCHECK_EQ(launch_files.size(), intent.files->size());
}
// Files for Web Share intents are created by the browser in
// a .WebShare directory, with generated file names and file urls - see
// //chrome/browser/webshare/chromeos/sharesheet_client.cc
for (size_t i = 0; i < intent.files->size(); ++i) {
const apps::mojom::IntentFilePtr& file = (*intent.files)[i];
const std::string& mime_type = file->mime_type.has_value()
? file->mime_type.value()
: intent.mime_type.value();
std::string name;
for (const apps::ShareTarget::Files& files : share_target.params.files) {
// Filter on MIME types. Chrome OS does not filter on file extensions.
// https://w3c.github.io/web-share-target/level-2/#dfn-accepted
if (base::ranges::any_of(
files.accept, [&mime_type](const auto& criteria) {
return !base::StartsWith(criteria, ".") &&
net::MatchesMimeType(criteria, mime_type);
})) {
name = files.name;
break;
}
}
if (name.empty())
continue;
storage::FileSystemURL file_system_url;
mojo::PendingRemote<network::mojom::DataPipeGetter> data_pipe_getter;
#if BUILDFLAG(IS_CHROMEOS_ASH)
storage::FileSystemContext* file_system_context =
file_manager::util::GetFileManagerFileSystemContext(
browser->profile());
file_system_url =
file_system_context->CrackURLInFirstPartyContext(file->url);
if (!file_system_url.is_valid()) {
// TODO(crbug.com/1166982): We could be more intelligent here and
// decide which cracking method to use based on the scheme.
auto file_system_url_and_handle =
arc::ShareInfoFileHandler::GetFileSystemURL(browser->profile(),
file->url);
file_system_url = file_system_url_and_handle.url;
if (!file_system_url.is_valid()) {
LOG(WARNING) << "Received unexpected file URL: " << file->url.spec();
continue;
}
constexpr int kBufSize = 16 * 1024;
FileStreamDataPipeGetter::Create(
/*receiver=*/data_pipe_getter.InitWithNewPipeAndPassReceiver(),
/*context=*/file_system_context,
/*url=*/file_system_url,
/*offset=*/0,
/*file_size=*/file->file_size,
/*buf_size=*/kBufSize);
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
const std::string filename =
(file->file_name.has_value() && !file->file_name->path().empty())
? (file->file_name->path().AsUTF8Unsafe())
: file_system_url.path().BaseName().AsUTF8Unsafe();
names.push_back(name);
if (launch_files.empty()) {
values.push_back(file_system_url.path().AsUTF8Unsafe());
} else {
values.push_back(launch_files[i].value());
}
is_value_file_uris.push_back(true);
filenames.push_back(filename);
types.push_back(mime_type);
data_pipe_getters.push_back(std::move(data_pipe_getter));
}
}
std::vector<SharedField> shared_fields =
ExtractSharedFields(share_target, intent);
for (const auto& shared_field : shared_fields) {
DCHECK(!shared_field.value.empty());
names.push_back(shared_field.name);
values.push_back(shared_field.value);
is_value_file_uris.push_back(false);
filenames.push_back(std::string());
types.push_back("text/plain");
data_pipe_getters.push_back(
mojo::PendingRemote<network::mojom::DataPipeGetter>());
}
if (share_target.enctype == apps::ShareTarget::Enctype::kMultipartFormData) {
const std::string boundary = net::GenerateMimeMultipartBoundary();
nav_params.extra_headers = base::StringPrintf(
"Content-Type: multipart/form-data; boundary=%s\r\n", boundary.c_str());
nav_params.post_data = web_share_target::ComputeMultipartBody(
names, values, is_value_file_uris, filenames, types,
std::move(data_pipe_getters), boundary);
} else {
const std::string serialization =
web_share_target::ComputeUrlEncodedBody(names, values);
if (share_target.method == apps::ShareTarget::Method::kPost) {
nav_params.extra_headers =
"Content-Type: application/x-www-form-urlencoded\r\n";
nav_params.post_data = network::ResourceRequestBody::CreateFromBytes(
serialization.c_str(), serialization.length());
} else {
url::Replacements<char> replacements;
replacements.SetQuery(serialization.c_str(),
url::Component(0, serialization.length()));
nav_params.url = nav_params.url.ReplaceComponents(replacements);
}
}
#else
// TODO(crbug.com/1153194): Support Web Share Target on Windows.
// TODO(crbug.com/1153195): Support Web Share Target on Mac.
NOTIMPLEMENTED();
#endif // BUILDFLAG(IS_CHROMEOS)
return nav_params;
}
} // namespace web_app