Index: chrome/browser/android/manifest_icon_selector.cc |
diff --git a/chrome/browser/android/manifest_icon_selector.cc b/chrome/browser/android/manifest_icon_selector.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d72e32be98c679581c84fb93476649b67b2bf841 |
--- /dev/null |
+++ b/chrome/browser/android/manifest_icon_selector.cc |
@@ -0,0 +1,142 @@ |
+// 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 "chrome/browser/android/manifest_icon_selector.h" |
+ |
+#include <limits> |
+ |
+#include "base/strings/utf_string_conversions.h" |
+#include "content/public/browser/web_contents.h" |
+#include "content/public/common/manifest.h" |
mlamouri (slow - plz ping)
2015/01/30 19:25:42
nit: already included in the header, fwiw.
gone
2015/01/30 20:08:11
Done.
|
+#include "net/base/mime_util.h" |
+#include "ui/gfx/screen.h" |
+#include "url/gurl.h" |
mlamouri (slow - plz ping)
2015/01/30 19:25:42
nit: ditto
gone
2015/01/30 20:08:11
Done.
|
+ |
+using content::Manifest; |
+ |
+ManifestIconSelector::ManifestIconSelector(content::WebContents* web_contents, |
+ int preferred_size_in_dp) |
+ : preferred_icon_size_in_px_(preferred_size_in_dp * |
+ gfx::Screen::GetScreenFor(web_contents->GetNativeView())-> |
+ GetPrimaryDisplay().device_scale_factor()) { |
+} |
+ |
+bool ManifestIconSelector::IconSizesContainsPreferredSize( |
+ const std::vector<gfx::Size>& sizes) const { |
+ for (size_t i = 0; i < sizes.size(); ++i) { |
+ if (sizes[i].height() != sizes[i].width()) |
+ continue; |
+ if (sizes[i].width() == preferred_icon_size_in_px_) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool ManifestIconSelector::IconSizesContainsAny( |
+ const std::vector<gfx::Size>& sizes) const { |
+ for (size_t i = 0; i < sizes.size(); ++i) { |
+ if (sizes[i].IsEmpty()) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+GURL ManifestIconSelector::FindBestMatchingIcon( |
+ const std::vector<Manifest::Icon>& icons, float density) const { |
+ GURL url; |
+ int best_delta = std::numeric_limits<int>::min(); |
+ |
+ for (size_t i = 0; i < icons.size(); ++i) { |
+ if (icons[i].density != density) |
+ continue; |
+ |
+ const std::vector<gfx::Size>& sizes = icons[i].sizes; |
+ for (size_t j = 0; j < sizes.size(); ++j) { |
+ if (sizes[j].height() != sizes[j].width()) |
+ continue; |
+ int delta = sizes[j].width() - preferred_icon_size_in_px_; |
+ if (delta == 0) |
+ return icons[i].src; |
+ if (best_delta > 0 && delta < 0) |
+ continue; |
+ if ((best_delta > 0 && delta < best_delta) || |
+ (best_delta < 0 && delta > best_delta)) { |
+ url = icons[i].src; |
+ best_delta = delta; |
+ } |
+ } |
+ } |
+ |
+ return url; |
+} |
+ |
+// static |
+std::vector<Manifest::Icon> ManifestIconSelector::FilterIconsByType( |
+ const std::vector<Manifest::Icon>& icons) { |
+ std::vector<Manifest::Icon> result; |
+ |
+ for (size_t i = 0; i < icons.size(); ++i) { |
+ if (icons[i].type.is_null() || |
+ net::IsSupportedImageMimeType( |
+ base::UTF16ToUTF8(icons[i].type.string()))) { |
+ result.push_back(icons[i]); |
+ } |
+ } |
+ |
+ return result; |
+} |
+ |
+GURL ManifestIconSelector::FindBestMatchingIcon( |
+ const std::vector<Manifest::Icon>& unfiltered_icons, |
+ content::WebContents* web_contents) const { |
+ const float device_scale_factor = |
+ gfx::Screen::GetScreenFor(web_contents->GetNativeView())-> |
+ GetPrimaryDisplay().device_scale_factor(); |
+ |
+ GURL url; |
+ std::vector<Manifest::Icon> icons = FilterIconsByType(unfiltered_icons); |
+ |
+ // The first pass is to find the ideal icon. That icon is of the right size |
+ // with the default density or the device's density. |
+ for (size_t i = 0; i < icons.size(); ++i) { |
+ if (icons[i].density == device_scale_factor && |
+ IconSizesContainsPreferredSize(icons[i].sizes)) { |
+ return icons[i].src; |
+ } |
+ |
+ // If there is an icon with the right size but not the right density, keep |
+ // it on the side and only use it if nothing better is found. |
+ if (icons[i].density == Manifest::Icon::kDefaultDensity && |
+ IconSizesContainsPreferredSize(icons[i].sizes)) { |
+ url = icons[i].src; |
+ } |
+ } |
+ |
+ // The second pass is to find an icon with 'any'. The current device scale |
+ // factor is preferred. Otherwise, the default scale factor is used. |
+ for (size_t i = 0; i < icons.size(); ++i) { |
+ if (icons[i].density == device_scale_factor && |
+ IconSizesContainsAny(icons[i].sizes)) { |
+ return icons[i].src; |
+ } |
+ |
+ // If there is an icon with 'any' but not the right density, keep it on the |
+ // side and only use it if nothing better is found. |
+ if (icons[i].density == Manifest::Icon::kDefaultDensity && |
+ IconSizesContainsAny(icons[i].sizes)) { |
+ url = icons[i].src; |
+ } |
+ } |
+ |
+ // The last pass will try to find the best suitable icon for the device's |
+ // scale factor. If none, another pass will be run using kDefaultDensity. |
+ if (!url.is_valid()) |
+ url = FindBestMatchingIcon(icons, device_scale_factor); |
+ if (!url.is_valid()) |
+ url = FindBestMatchingIcon(icons, Manifest::Icon::kDefaultDensity); |
+ |
+ return url; |
+} |