blob: 4deb044b402e5ab8da87d124b453f4c018b76961 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/common/manifest_handlers/file_handler_info.h"
#include <stddef.h>
#include <memory>
#include "base/feature_list.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/web_file_handlers_info.h"
namespace extensions {
namespace keys = manifest_keys;
namespace errors = manifest_errors;
namespace {
const int kMaxTypeAndExtensionHandlers = 200;
const char kNotRecognized[] = "'%s' is not a recognized file handler property.";
bool IsSupportedVerb(const std::string& verb) {
return verb == apps::file_handler_verbs::kOpenWith ||
verb == apps::file_handler_verbs::kAddTo ||
verb == apps::file_handler_verbs::kPackWith ||
verb == apps::file_handler_verbs::kShareWith;
}
bool LoadFileHandler(const std::string& handler_id,
const base::Value::Dict& handler_info,
FileHandlersInfo* file_handlers,
std::u16string* error,
std::vector<InstallWarning>* install_warnings) {
DCHECK(error);
apps::FileHandlerInfo handler;
handler.id = handler_id;
const base::Value* mime_types = handler_info.Find(keys::kFileHandlerTypes);
if (mime_types != nullptr && !mime_types->is_list()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidFileHandlerType, handler_id);
return false;
}
const base::Value* file_extensions =
handler_info.Find(keys::kFileHandlerExtensions);
if (file_extensions != nullptr && !file_extensions->is_list()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidFileHandlerExtension, handler_id);
return false;
}
handler.include_directories = false;
const base::Value* include_directories =
handler_info.Find(keys::kFileHandlerIncludeDirectories);
if (include_directories != nullptr) {
if (include_directories->is_bool()) {
handler.include_directories = include_directories->GetBool();
} else {
*error = errors::kInvalidFileHandlerIncludeDirectories;
return false;
}
}
handler.verb = apps::file_handler_verbs::kOpenWith;
const base::Value* file_handler = handler_info.Find(keys::kFileHandlerVerb);
if (file_handler != nullptr) {
if (file_handler->is_string() &&
IsSupportedVerb(file_handler->GetString())) {
handler.verb = file_handler->GetString();
} else {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidFileHandlerVerb, handler_id);
return false;
}
}
if ((!mime_types || mime_types->GetList().empty()) &&
(!file_extensions || file_extensions->GetList().empty()) &&
!handler.include_directories) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidFileHandlerNoTypeOrExtension,
handler_id);
return false;
}
if (mime_types) {
const base::Value::List& list = mime_types->GetList();
for (size_t i = 0; i < list.size(); ++i) {
if (!list[i].is_string()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidFileHandlerTypeElement, handler_id,
base::NumberToString(i));
return false;
}
handler.types.insert(list[i].GetString());
}
}
if (file_extensions) {
const base::Value::List& list = file_extensions->GetList();
for (size_t i = 0; i < list.size(); ++i) {
if (!list[i].is_string()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidFileHandlerExtensionElement, handler_id,
base::NumberToString(i));
return false;
}
handler.extensions.insert(list[i].GetString());
}
}
file_handlers->push_back(handler);
// Check for unknown keys.
for (auto entry : handler_info) {
if (entry.first != keys::kFileHandlerExtensions &&
entry.first != keys::kFileHandlerTypes &&
entry.first != keys::kFileHandlerIncludeDirectories &&
entry.first != keys::kFileHandlerVerb) {
install_warnings->emplace_back(
base::StringPrintf(kNotRecognized, entry.first.c_str()),
keys::kFileHandlers, entry.first);
}
}
return true;
}
} // namespace
FileHandlerMatch::FileHandlerMatch() = default;
FileHandlerMatch::~FileHandlerMatch() = default;
FileHandlers::FileHandlers() = default;
FileHandlers::~FileHandlers() = default;
// static
const FileHandlersInfo* FileHandlers::GetFileHandlers(
const Extension* extension) {
CHECK(extension);
if (WebFileHandlers::SupportsWebFileHandlers(*extension)) {
return nullptr;
}
FileHandlers* info = static_cast<FileHandlers*>(
extension->GetManifestData(keys::kFileHandlers));
return info ? &info->file_handlers : nullptr;
}
FileHandlersParser::FileHandlersParser() = default;
FileHandlersParser::~FileHandlersParser() = default;
bool FileHandlersParser::Validate(const Extension& extension,
std::string* error,
std::vector<InstallWarning>* warnings) const {
// Web File Handlers.
if (extension.manifest_version() >= 3) {
return WebFileHandlersParser().Validate(extension, error, warnings);
}
return true;
}
bool FileHandlersParser::Parse(Extension* extension, std::u16string* error) {
// If this is an MV3 extension, use the generated `file_handlers` object.
if (extension->manifest_version() >= 3) {
return WebFileHandlersParser().Parse(extension, error);
}
std::unique_ptr<FileHandlers> info(new FileHandlers);
const base::Value::Dict* all_handlers =
extension->manifest()->available_values().FindDict(keys::kFileHandlers);
if (!all_handlers) {
*error = errors::kInvalidFileHandlers;
return false;
}
std::vector<InstallWarning> install_warnings;
for (auto entry : *all_handlers) {
if (!entry.second.is_dict()) {
*error = errors::kInvalidFileHandlers;
return false;
}
if (!LoadFileHandler(entry.first, entry.second.GetDict(),
&info->file_handlers, error, &install_warnings)) {
return false;
}
}
int filter_count = 0;
for (const auto& iter : info->file_handlers) {
filter_count += iter.types.size() + iter.extensions.size();
}
if (filter_count > kMaxTypeAndExtensionHandlers) {
*error = errors::kInvalidFileHandlersTooManyTypesAndExtensions;
return false;
}
extension->SetManifestData(keys::kFileHandlers, std::move(info));
extension->AddInstallWarnings(std::move(install_warnings));
return true;
}
base::span<const char* const> FileHandlersParser::Keys() const {
static constexpr const char* kKeys[] = {keys::kFileHandlers};
return kKeys;
}
} // namespace extensions