| 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..deef230222cac32f325609f674eeb706fc30cb82
|
| --- /dev/null
|
| +++ b/chrome/browser/android/manifest_icon_selector.cc
|
| @@ -0,0 +1,147 @@
|
| +// 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 "net/base/mime_util.h"
|
| +#include "ui/gfx/screen.h"
|
| +
|
| +using content::Manifest;
|
| +
|
| +// static
|
| +bool ManifestIconSelector::IconSizesContainsPreferredSize(
|
| + const std::vector<gfx::Size>& sizes,
|
| + int preferred_icon_size_in_px) {
|
| + 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;
|
| +}
|
| +
|
| +// static
|
| +bool ManifestIconSelector::IconSizesContainsAny(
|
| + const std::vector<gfx::Size>& sizes) {
|
| + for (size_t i = 0; i < sizes.size(); ++i) {
|
| + if (sizes[i].IsEmpty())
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// static
|
| +GURL ManifestIconSelector::FindBestMatchingIcon(
|
| + const std::vector<Manifest::Icon>& icons,
|
| + float density,
|
| + int preferred_icon_size_in_px) {
|
| + 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;
|
| +}
|
| +
|
| +// static
|
| +GURL ManifestIconSelector::FindBestMatchingIcon(
|
| + const std::vector<Manifest::Icon>& unfiltered_icons,
|
| + const float preferred_size_in_dp,
|
| + content::WebContents* web_contents) {
|
| + const float device_scale_factor =
|
| + gfx::Screen::GetScreenFor(web_contents->GetNativeView())->
|
| + GetPrimaryDisplay().device_scale_factor();
|
| + const float preferred_size_in_px =
|
| + preferred_size_in_dp * 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, preferred_size_in_px)) {
|
| + 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, preferred_size_in_px)) {
|
| + 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,
|
| + preferred_size_in_px);
|
| + if (!url.is_valid())
|
| + url = FindBestMatchingIcon(icons,
|
| + Manifest::Icon::kDefaultDensity,
|
| + preferred_size_in_px);
|
| +
|
| + return url;
|
| +}
|
|
|