0

Extensions: IconVariants: Prefer icon_variants over icons

Doc:
https://docs.google.com/document/d/1fY2sS0JucA87rj0itmcUqJG7HUxiBfLwTnzqt7J28SQ/edit?usp=sharing

Bug: chromium:344639840
Change-Id: Id76437b15316c72aac11da1be2c39cc0aec41713
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6288312
Reviewed-by: David Bertoni <dbertoni@chromium.org>
Commit-Queue: Solomon Kinard <solomonkinard@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1424221}
This commit is contained in:
Solomon Kinard
2025-02-24 16:21:28 -08:00
committed by Chromium LUCI CQ
parent a39205c68f
commit 1f29b34027
7 changed files with 113 additions and 17 deletions

@ -37,6 +37,16 @@ class ExtensionIconVariant {
return diagnostics_;
}
using Size = short;
using Path = std::string;
// Getters.
const std::optional<Path>& GetAny() const { return any_; }
const std::set<ColorScheme>& GetColorSchemes() const {
return color_schemes_;
}
const base::flat_map<Size, Path>& GetSizes() const { return sizes_; }
private:
// Helper methods that add to `this` object if the parameter is valid.
void MaybeAddColorSchemes(const base::Value& value);
@ -46,9 +56,6 @@ class ExtensionIconVariant {
// Either `any` or `<size>` keys must have at least one value.
bool IsValid() const;
using Size = short;
using Path = std::string;
// The any key can have a path that's for any size.
std::optional<Path> any_;

@ -51,7 +51,7 @@ void ExtensionIconVariants::Parse(const base::Value::List* list) {
}
bool ExtensionIconVariants::IsEmpty() const {
return list_.size() == 0;
return list_.empty();
}
void ExtensionIconVariants::Add(

@ -9,12 +9,13 @@
#include <vector>
#include "base/values.h"
#include "extensions/common/icons/extension_icon_set.h"
#include "extensions/common/icons/extension_icon_variant.h"
#include "extensions/common/icons/extension_icon_variants_diagnostics.h"
namespace extensions {
// Representation of the `icon_variants` key anywhere in manifest.json. It could
// Representation of the `icon_variants` key defined in manifest.json. It could
// be a top level key or a subkey of `action`.
class ExtensionIconVariants {
public:
@ -37,6 +38,8 @@ class ExtensionIconVariants {
return diagnostics_;
}
const std::vector<ExtensionIconVariant>& GetList() const { return list_; }
private:
std::vector<ExtensionIconVariant> list_;

@ -5,14 +5,27 @@
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "base/version_info/channel.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/common/icons/extension_icon_set.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/common/manifest_test.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
using IconVariantsFeatureFreeManifestTest = ManifestTest;
// Test that icon_variants doesn't create an error when the feature isn't
// enabled, even in the event of warnings.
TEST_F(IconVariantsFeatureFreeManifestTest, Warnings) {
LoadAndExpectWarnings("icon_variants.json",
{"'icon_variants' requires canary channel or newer, "
"but this is the stable channel."});
}
class IconVariantsManifestTest : public ManifestTest {
public:
IconVariantsManifestTest() {
@ -165,14 +178,26 @@ TEST_F(IconVariantsManifestTest, Errors) {
}
}
using IconVariantsFeatureFreeManifestTest = ManifestTest;
TEST_F(IconVariantsManifestTest, PreferIconVariantsOverIcons) {
ManifestData manifest_data = ManifestData::FromJSON(
R"({
"name": "test",
"version": "1",
"manifest_version": 3,
"icons": {
"16": "icons.16.png"
},
"icon_variants": [{
"16": "icon_variants.16.png"
}]
})");
scoped_refptr<extensions::Extension> extension(
LoadAndExpectSuccess(manifest_data));
const ExtensionIconSet& icons = IconsInfo::GetIcons(extension.get());
// Test that icon_variants doesn't create an error, even in the event of
// warnings.
TEST_F(IconVariantsFeatureFreeManifestTest, Warnings) {
LoadAndExpectWarnings("icon_variants.json",
{"'icon_variants' requires canary channel or newer, "
"but this is the stable channel."});
EXPECT_EQ("icon_variants.16.png",
icons.Get(extension_misc::EXTENSION_ICON_BITTY,
ExtensionIconSet::Match::kExactly));
}
} // namespace extensions

@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include "base/lazy_instance.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/api/icon_variants.h"
@ -14,9 +15,15 @@
#include "extensions/common/icons/extension_icon_variants.h"
#include "extensions/common/icons/extension_icon_variants_diagnostics.h"
#include "extensions/common/manifest_constants.h"
#include "ui/gfx/color_utils.h"
namespace extensions {
namespace {
static base::LazyInstance<ExtensionIconSet>::DestructorAtExit g_empty_icon_set =
LAZY_INSTANCE_INITIALIZER;
} // namespace
IconVariantsInfo::IconVariantsInfo() = default;
IconVariantsInfo::~IconVariantsInfo() = default;
@ -94,6 +101,40 @@ const IconVariantsInfo* IconVariantsInfo::GetIconVariants(
extension->GetManifestData(IconVariantsManifestKeys::kIconVariants));
}
void IconVariantsInfo::InitializeIconSets() {
for (const auto& icon_variant : icon_variants->GetList()) {
const auto color_schemes = icon_variant.GetColorSchemes();
const auto sizes = icon_variant.GetSizes();
// TODO(crbug.com/344639840): Support any, e.g. any = icon_variant.GetAny();
for (const auto& size : sizes) {
// Add the size path pair to both extension icon sets if unspecified.
if (color_schemes.empty()) {
dark_.Add(size.first, size.second);
light_.Add(size.first, size.second);
continue;
}
if (color_schemes.contains(ExtensionIconVariant::ColorScheme::kDark)) {
dark_.Add(size.first, size.second);
}
if (color_schemes.contains(ExtensionIconVariant::ColorScheme::kLight)) {
light_.Add(size.first, size.second);
}
}
}
}
const ExtensionIconSet& IconVariantsInfo::Get() const {
if (!icon_variants) {
g_empty_icon_set.Get();
}
// TODO(crbug.com/344639840): Determine the current browser theme color.
return light_;
}
// TODO(crbug.com/41419485): Add more test coverage for warnings and errors.
bool IconVariantsHandler::Parse(Extension* extension, std::u16string* error) {
DCHECK(extension);
@ -146,6 +187,7 @@ bool IconVariantsHandler::Parse(Extension* extension, std::u16string* error) {
std::unique_ptr<IconVariantsInfo> icon_variants_info(
std::make_unique<IconVariantsInfo>());
icon_variants_info->icon_variants = std::move(icon_variants);
icon_variants_info->InitializeIconSets();
extension->SetManifestData(keys::kIconVariants,
std::move(icon_variants_info));
@ -156,7 +198,7 @@ bool IconVariantsHandler::Validate(
const Extension* extension,
std::string* error,
std::vector<InstallWarning>* warnings) const {
// TODO(crbug.com/41419485): Validate icons.
// TODO(crbug.com/41419485): Validate icon existence.
return true;
}

@ -18,14 +18,25 @@ struct IconVariantsInfo : public Extension::ManifestData {
IconVariantsInfo();
~IconVariantsInfo() override;
// A map of mode to ExtensionIconSet, which represents the declared icons.
std::unique_ptr<ExtensionIconVariants> icon_variants;
// Returns the icon set for the given `extension`.
// Returns whether `icon_variants` are defined for the given `extension`.
static bool HasIconVariants(const Extension* extension);
// Get IconVariants for the given `extension`, if they exist.
static const IconVariantsInfo* GetIconVariants(const Extension* extension);
// Retrieve a matching ExtensionIconSet.
const ExtensionIconSet& Get() const;
// Data structure for `icon_variants`, based on icon_variants.idl.
std::unique_ptr<ExtensionIconVariants> icon_variants;
// Populate member variable extension sets from `icon_variants`.
void InitializeIconSets();
private:
// Allow for easy access to the theme-related sets
ExtensionIconSet dark_;
ExtensionIconSet light_;
};
// Parses the "icon_variants" manifest key.

@ -17,6 +17,7 @@
#include "extensions/common/image_util.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handler_helpers.h"
#include "extensions/common/manifest_handlers/icon_variants_handler.h"
#include "extensions/strings/grit/extensions_strings.h"
#include "ui/gfx/geometry/size.h"
@ -30,6 +31,13 @@ static base::LazyInstance<ExtensionIconSet>::DestructorAtExit g_empty_icon_set =
// static
const ExtensionIconSet& IconsInfo::GetIcons(const Extension* extension) {
DCHECK(extension);
// Prefer `icon_variants` over `icons`.
const IconVariantsInfo* icon_variants_info =
IconVariantsInfo::GetIconVariants(extension);
if (icon_variants_info) {
return icon_variants_info->Get();
}
IconsInfo* info = static_cast<IconsInfo*>(
extension->GetManifestData(keys::kIcons));
return info ? info->icons : g_empty_icon_set.Get();