blob: 9c43f00875f346f2ae55e1a708511e750566dcf1 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
#include <memory>
#include "base/files/file_util.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/extensions/api/chrome_url_overrides.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/manifest_handlers/shared_module_info.h"
#include "extensions/common/manifest_url_handlers.h"
#include "extensions/common/permissions/api_permission.h"
namespace extensions {
namespace keys = manifest_keys;
namespace errors = manifest_errors;
namespace {
const char kOverrideExtentUrlPatternFormat[] = "chrome://%s/*";
using ChromeUrlOverridesKeys = api::chrome_url_overrides::ManifestKeys;
} // namespace
namespace chrome_manifest_urls {
const GURL& GetDevToolsPage(const Extension* extension) {
return ManifestURL::Get(extension, keys::kDevToolsPage);
}
}
URLOverrides::URLOverrides() = default;
URLOverrides::~URLOverrides() = default;
// static
const URLOverrides::URLOverrideMap& URLOverrides::GetChromeURLOverrides(
const Extension* extension) {
static const base::NoDestructor<URLOverrides::URLOverrideMap>
empty_url_overrides;
URLOverrides* url_overrides = static_cast<URLOverrides*>(
extension->GetManifestData(ChromeUrlOverridesKeys::kChromeUrlOverrides));
return url_overrides ? url_overrides->chrome_url_overrides_
: *empty_url_overrides;
}
DevToolsPageHandler::DevToolsPageHandler() = default;
DevToolsPageHandler::~DevToolsPageHandler() = default;
bool DevToolsPageHandler::Parse(Extension* extension, std::u16string* error) {
std::unique_ptr<ManifestURL> manifest_url(new ManifestURL);
const std::string* devtools_str =
extension->manifest()->FindStringPath(keys::kDevToolsPage);
if (!devtools_str) {
*error = errors::kInvalidDevToolsPage;
return false;
}
GURL url = extension->GetResourceURL(*devtools_str);
// SharedModuleInfo::IsImportedPath() does not require knowledge of data from
// extension, so we can call it right here in Parse() and not Validate() and
// do not need to specify DevToolsPageHandler::PrerequisiteKeys()
const bool is_extension_url = url.SchemeIs(kExtensionScheme) &&
url.host_piece() == extension->id() &&
!SharedModuleInfo::IsImportedPath(url.path());
if (!is_extension_url) {
*error = errors::kInvalidDevToolsPage;
return false;
}
manifest_url->url_ = std::move(url);
extension->SetManifestData(keys::kDevToolsPage, std::move(manifest_url));
PermissionsParser::AddAPIPermission(extension,
mojom::APIPermissionID::kDevtools);
return true;
}
base::span<const char* const> DevToolsPageHandler::Keys() const {
static constexpr const char* kKeys[] = {keys::kDevToolsPage};
return kKeys;
}
bool DevToolsPageHandler::Validate(
const Extension* extension,
std::string* error,
std::vector<InstallWarning>* warnings) const {
const GURL& url = chrome_manifest_urls::GetDevToolsPage(extension);
const base::FilePath relative_path =
file_util::ExtensionURLToRelativeFilePath(url);
const base::FilePath resource_path =
extension->GetResource(relative_path).GetFilePath();
if (resource_path.empty() || !base::PathExists(resource_path)) {
const std::string message = ErrorUtils::FormatErrorMessage(
errors::kFileNotFound, relative_path.AsUTF8Unsafe());
warnings->emplace_back(message, keys::kDevToolsPage);
}
return true;
}
URLOverridesHandler::URLOverridesHandler() = default;
URLOverridesHandler::~URLOverridesHandler() = default;
bool URLOverridesHandler::Parse(Extension* extension, std::u16string* error) {
ChromeUrlOverridesKeys manifest_keys;
if (!ChromeUrlOverridesKeys::ParseFromDictionary(
extension->manifest()->available_values(), manifest_keys, *error)) {
return false;
}
using UrlOverrideInfo = api::chrome_url_overrides::UrlOverrideInfo;
auto url_overrides = std::make_unique<URLOverrides>();
auto property_map =
std::map<const char*,
std::reference_wrapper<const std::optional<std::string>>>{
{UrlOverrideInfo::kNewtab,
std::ref(manifest_keys.chrome_url_overrides.newtab)},
{UrlOverrideInfo::kBookmarks,
std::ref(manifest_keys.chrome_url_overrides.bookmarks)},
{UrlOverrideInfo::kHistory,
std::ref(manifest_keys.chrome_url_overrides.history)},
{UrlOverrideInfo::kActivationmessage,
std::ref(manifest_keys.chrome_url_overrides.activationmessage)},
{UrlOverrideInfo::kKeyboard,
std::ref(manifest_keys.chrome_url_overrides.keyboard)}};
for (const auto& property : property_map) {
if (!property.second.get())
continue;
// Replace the entry with a fully qualified chrome-extension:// URL.
url_overrides->chrome_url_overrides_[property.first] =
extension->GetResourceURL(*property.second.get());
// For component extensions, add override URL to extent patterns.
if (extension->is_legacy_packaged_app() &&
extension->location() == mojom::ManifestLocation::kComponent) {
URLPattern pattern(URLPattern::SCHEME_CHROMEUI);
std::string url =
base::StringPrintf(kOverrideExtentUrlPatternFormat, property.first);
if (pattern.Parse(url) != URLPattern::ParseResult::kSuccess) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidURLPatternError, url);
return false;
}
extension->AddWebExtentPattern(pattern);
}
}
// An extension may override at most one page.
if (url_overrides->chrome_url_overrides_.size() > 1u) {
*error = errors::kMultipleOverrides;
return false;
}
// If this is an NTP override extension, add the NTP override permission.
if (manifest_keys.chrome_url_overrides.newtab) {
PermissionsParser::AddAPIPermission(
extension, mojom::APIPermissionID::kNewTabPageOverride);
}
extension->SetManifestData(ChromeUrlOverridesKeys::kChromeUrlOverrides,
std::move(url_overrides));
return true;
}
bool URLOverridesHandler::Validate(
const Extension* extension,
std::string* error,
std::vector<InstallWarning>* warnings) const {
const URLOverrides::URLOverrideMap& overrides =
URLOverrides::GetChromeURLOverrides(extension);
if (overrides.empty())
return true;
for (const auto& entry : overrides) {
base::FilePath relative_path =
file_util::ExtensionURLToRelativeFilePath(entry.second);
base::FilePath resource_path =
extension->GetResource(relative_path).GetFilePath();
if (resource_path.empty() || !base::PathExists(resource_path)) {
*error = ErrorUtils::FormatErrorMessage(errors::kFileNotFound,
relative_path.AsUTF8Unsafe());
return false;
}
}
return true;
}
base::span<const char* const> URLOverridesHandler::Keys() const {
static constexpr const char* kKeys[] = {
ChromeUrlOverridesKeys::kChromeUrlOverrides};
return kKeys;
}
} // namespace extensions