webapps: allow callers of icon downloader/selector to specify a minimum size
[chromium-blink-merge.git] / chrome / browser / manifest / manifest_icon_selector.cc
blob21e0d460d2f585cb3919b7cd7aaa626c8a9e22aa
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/manifest/manifest_icon_selector.h"
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
11 #include "base/strings/utf_string_conversions.h"
12 #include "components/mime_util/mime_util.h"
13 #include "content/public/browser/web_contents.h"
14 #include "ui/gfx/screen.h"
16 using content::Manifest;
18 ManifestIconSelector::ManifestIconSelector(int ideal_icon_size_in_px,
19 int minimum_icon_size_in_px)
20 : ideal_icon_size_in_px_(ideal_icon_size_in_px),
21 minimum_icon_size_in_px_(minimum_icon_size_in_px) {
24 bool ManifestIconSelector::IconSizesContainsPreferredSize(
25 const std::vector<gfx::Size>& sizes) {
26 for (size_t i = 0; i < sizes.size(); ++i) {
27 if (sizes[i].height() != sizes[i].width())
28 continue;
29 if (sizes[i].width() == ideal_icon_size_in_px_)
30 return true;
33 return false;
36 bool ManifestIconSelector::IconSizesContainsBiggerThanMinimumSize(
37 const std::vector<gfx::Size>& sizes) {
38 for (size_t i = 0; i < sizes.size(); ++i) {
39 if (sizes[i].height() != sizes[i].width())
40 continue;
41 if (sizes[i].width() >= minimum_icon_size_in_px_)
42 return true;
44 return false;
47 int ManifestIconSelector::FindBestMatchingIconForDensity(
48 const std::vector<content::Manifest::Icon>& icons,
49 float density) {
50 int best_index = -1;
51 int best_delta = std::numeric_limits<int>::min();
53 for (size_t i = 0; i < icons.size(); ++i) {
54 if (icons[i].density != density)
55 continue;
57 const std::vector<gfx::Size>& sizes = icons[i].sizes;
58 for (size_t j = 0; j < sizes.size(); ++j) {
59 if (sizes[j].height() != sizes[j].width())
60 continue;
61 int delta = sizes[j].width() - ideal_icon_size_in_px_;
62 if (delta == 0)
63 return i;
64 if (best_delta > 0 && delta < 0)
65 continue;
66 if ((best_delta > 0 && delta < best_delta) ||
67 (best_delta < 0 && delta > best_delta)) {
68 best_index = i;
69 best_delta = delta;
74 return best_index;
77 int ManifestIconSelector::FindBestMatchingIcon(
78 const std::vector<content::Manifest::Icon>& icons,
79 float density) {
80 int best_index = -1;
82 // The first pass is to find the ideal icon. That icon is of the right size
83 // with the default density or the device's density.
84 for (size_t i = 0; i < icons.size(); ++i) {
85 if (icons[i].density == density &&
86 IconSizesContainsPreferredSize(icons[i].sizes)) {
87 return i;
90 // If there is an icon with the right size but not the right density, keep
91 // it on the side and only use it if nothing better is found.
92 if (icons[i].density == Manifest::Icon::kDefaultDensity &&
93 IconSizesContainsPreferredSize(icons[i].sizes)) {
94 best_index = i;
98 if (best_index != -1)
99 return best_index;
101 // The second pass is to find an icon with 'any'. The current device scale
102 // factor is preferred. Otherwise, the default scale factor is used.
103 for (size_t i = 0; i < icons.size(); ++i) {
104 if (icons[i].density == density &&
105 IconSizesContainsAny(icons[i].sizes)) {
106 return i;
109 // If there is an icon with 'any' but not the right density, keep it on the
110 // side and only use it if nothing better is found.
111 if (icons[i].density == Manifest::Icon::kDefaultDensity &&
112 IconSizesContainsAny(icons[i].sizes)) {
113 best_index = i;
117 if (best_index != -1)
118 return best_index;
120 // The last pass will try to find the best suitable icon for the device's
121 // scale factor. If none, another pass will be run using kDefaultDensity.
122 best_index = FindBestMatchingIconForDensity(icons, density);
123 if (best_index != -1 &&
124 IconSizesContainsBiggerThanMinimumSize(icons[best_index].sizes))
125 return best_index;
127 best_index = FindBestMatchingIconForDensity(icons,
128 Manifest::Icon::kDefaultDensity);
129 if (best_index != -1 &&
130 IconSizesContainsBiggerThanMinimumSize(icons[best_index].sizes))
131 return best_index;
133 return -1;
137 // static
138 bool ManifestIconSelector::IconSizesContainsAny(
139 const std::vector<gfx::Size>& sizes) {
140 for (size_t i = 0; i < sizes.size(); ++i) {
141 if (sizes[i].IsEmpty())
142 return true;
144 return false;
147 // static
148 std::vector<Manifest::Icon> ManifestIconSelector::FilterIconsByType(
149 const std::vector<content::Manifest::Icon>& icons) {
150 std::vector<Manifest::Icon> result;
152 for (size_t i = 0; i < icons.size(); ++i) {
153 if (icons[i].type.is_null() ||
154 mime_util::IsSupportedImageMimeType(
155 base::UTF16ToUTF8(icons[i].type.string()))) {
156 result.push_back(icons[i]);
160 return result;
163 // static
164 GURL ManifestIconSelector::FindBestMatchingIcon(
165 const std::vector<Manifest::Icon>& unfiltered_icons,
166 const int ideal_icon_size_in_dp,
167 const int minimum_icon_size_in_dp,
168 const gfx::Screen* screen) {
169 DCHECK(minimum_icon_size_in_dp <= ideal_icon_size_in_dp);
171 const float device_scale_factor =
172 screen->GetPrimaryDisplay().device_scale_factor();
173 const int ideal_icon_size_in_px =
174 static_cast<int>(round(ideal_icon_size_in_dp * device_scale_factor));
175 const int minimum_icon_size_in_px =
176 static_cast<int>(round(minimum_icon_size_in_dp * device_scale_factor));
178 std::vector<Manifest::Icon> icons =
179 ManifestIconSelector::FilterIconsByType(unfiltered_icons);
181 ManifestIconSelector selector(ideal_icon_size_in_px,
182 minimum_icon_size_in_px);
183 int index = selector.FindBestMatchingIcon(icons, device_scale_factor);
184 if (index == -1)
185 return GURL();
186 return icons[index].src;