blob: e0fa8f353e9e947098496436d81fe85ef3c4718f [file] [log] [blame]
// Copyright 2015 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 "third_party/blink/public/common/manifest/manifest_icon_selector.h"
#include <limits>
#include "base/containers/contains.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/blink/public/common/mime_util/mime_util.h"
namespace blink {
// static
BLINK_COMMON_EXPORT GURL ManifestIconSelector::FindBestMatchingSquareIcon(
const std::vector<blink::Manifest::ImageResource>& icons,
int ideal_icon_size_in_px,
int minimum_icon_size_in_px,
blink::mojom::ManifestImageResource_Purpose purpose) {
return FindBestMatchingIcon(icons, ideal_icon_size_in_px,
minimum_icon_size_in_px,
1 /*max_width_to_height_ratio */, purpose);
}
// static
BLINK_COMMON_EXPORT GURL ManifestIconSelector::FindBestMatchingIcon(
const std::vector<blink::Manifest::ImageResource>& icons,
int ideal_icon_height_in_px,
int minimum_icon_height_in_px,
float max_width_to_height_ratio,
blink::mojom::ManifestImageResource_Purpose purpose) {
DCHECK_LE(minimum_icon_height_in_px, ideal_icon_height_in_px);
DCHECK_GE(max_width_to_height_ratio, 1.0);
// Icon with exact matching size has priority over icon with size "any", which
// has priority over icon with closest matching size.
int latest_size_any_index = -1;
int closest_size_match_index = -1;
int best_delta_in_size = std::numeric_limits<int>::min();
for (size_t i = 0; i < icons.size(); ++i) {
const auto& icon = icons[i];
// Check for supported image MIME types.
if (!icon.type.empty()) {
std::string type = base::UTF16ToUTF8(icon.type);
if (!(blink::IsSupportedImageMimeType(base::UTF16ToUTF8(icon.type)) ||
// The following condition is intended to support image/svg+xml:
(base::StartsWith(base::UTF16ToUTF8(icon.type), "image/",
base::CompareCase::SENSITIVE) &&
blink::IsSupportedNonImageMimeType(
base::UTF16ToUTF8(icon.type))))) {
continue;
}
}
// Check for icon purpose.
if (!base::Contains(icon.purpose, purpose))
continue;
// Check for size constraints.
for (const gfx::Size& size : icon.sizes) {
// Check for size "any". Return this icon if no better one is found.
if (size.IsEmpty()) {
latest_size_any_index = i;
continue;
}
// Check for minimum size.
if (size.height() < minimum_icon_height_in_px)
continue;
// Check for width to height ratio.
float width = static_cast<float>(size.width());
float height = static_cast<float>(size.height());
DCHECK_GT(height, 0);
float ratio = width / height;
if (ratio < 1 || ratio > max_width_to_height_ratio)
continue;
// According to the spec when there are multiple equally appropriate icons
// we should choose the last one declared in the list:
// https://w3c.github.io/manifest/#icons-member
if (size.height() == ideal_icon_height_in_px) {
closest_size_match_index = i;
best_delta_in_size = 0;
continue;
}
// Check for closest match.
int delta = size.height() - ideal_icon_height_in_px;
// Smallest icon larger than ideal size has priority over largest icon
// smaller than ideal size.
if (best_delta_in_size > 0 && delta < 0)
continue;
if ((best_delta_in_size > 0 && delta < best_delta_in_size) ||
(best_delta_in_size < 0 && delta > best_delta_in_size)) {
closest_size_match_index = i;
best_delta_in_size = delta;
}
}
}
if (best_delta_in_size == 0) {
DCHECK_NE(closest_size_match_index, -1);
return icons[closest_size_match_index].src;
}
if (latest_size_any_index != -1)
return icons[latest_size_any_index].src;
if (closest_size_match_index != -1)
return icons[closest_size_match_index].src;
return GURL();
}
} // namespace blink