Home | History | Annotate | Download | only in android
      1 // Copyright 2014 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.
      4 
      5 #include "chrome/browser/android/shortcut_helper.h"
      6 
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
      9 #include "content/public/browser/web_contents.h"
     10 #include "ui/gfx/screen.h"
     11 #include "ui/gfx/screen_type_delegate.h"
     12 
     13 // A dummy implementation of gfx::Screen, since ShortcutHelper needs access to
     14 // a gfx::Display's device scale factor.
     15 // This is inspired by web_contents_video_capture_device_unittest.cc
     16 // A bug has been opened to merge all those mocks: http://crbug.com/417227
     17 class FakeScreen : public gfx::Screen {
     18  public:
     19   FakeScreen() : display_(0x1337, gfx::Rect(0, 0, 2560, 1440)) {
     20   }
     21   virtual ~FakeScreen() {}
     22 
     23   void SetDisplayDeviceScaleFactor(float device_scale_factor) {
     24     display_.set_device_scale_factor(device_scale_factor);
     25   }
     26 
     27   // gfx::Screen implementation (only what's needed for testing).
     28   virtual bool IsDIPEnabled() OVERRIDE { return true; }
     29   virtual gfx::Point GetCursorScreenPoint() OVERRIDE { return gfx::Point(); }
     30   virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE { return NULL; }
     31   virtual gfx::NativeWindow GetWindowAtScreenPoint(
     32       const gfx::Point& point) OVERRIDE { return NULL; }
     33   virtual int GetNumDisplays() const OVERRIDE { return 1; }
     34   virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
     35     return std::vector<gfx::Display>(1, display_);
     36   }
     37   virtual gfx::Display GetDisplayNearestWindow(
     38       gfx::NativeView view) const OVERRIDE {
     39     return display_;
     40   }
     41   virtual gfx::Display GetDisplayNearestPoint(
     42       const gfx::Point& point) const OVERRIDE {
     43     return display_;
     44   }
     45   virtual gfx::Display GetDisplayMatching(
     46       const gfx::Rect& match_rect) const OVERRIDE {
     47     return display_;
     48   }
     49   virtual gfx::Display GetPrimaryDisplay() const OVERRIDE {
     50     return display_;
     51   }
     52   virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE {}
     53   virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE {}
     54 
     55  private:
     56   gfx::Display display_;
     57 
     58   DISALLOW_COPY_AND_ASSIGN(FakeScreen);
     59 };
     60 
     61 class ShortcutHelperTest : public ChromeRenderViewHostTestHarness  {
     62  protected:
     63   ShortcutHelperTest() : shortcut_helper_(NULL) {}
     64   virtual ~ShortcutHelperTest() {}
     65 
     66   static jobject CreateShortcutHelperJava(JNIEnv* env) {
     67     jclass clazz = env->FindClass("org/chromium/chrome/browser/ShortcutHelper");
     68     jmethodID constructor =
     69         env->GetMethodID(clazz, "<init>",
     70                          "(Landroid/content/Context;"
     71                              "Lorg/chromium/chrome/browser/Tab;)V");
     72     return env->NewObject(clazz, constructor, jobject(), jobject());
     73   }
     74 
     75   void ResetShorcutHelper() {
     76     if (shortcut_helper_)
     77       delete shortcut_helper_;
     78 
     79     JNIEnv* env = base::android::AttachCurrentThread();
     80     shortcut_helper_ =
     81         new ShortcutHelper(env, CreateShortcutHelperJava(env), web_contents());
     82   }
     83 
     84   virtual void SetUp() OVERRIDE {
     85     gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, &fake_screen_);
     86     ASSERT_EQ(&fake_screen_, gfx::Screen::GetNativeScreen());
     87 
     88     ChromeRenderViewHostTestHarness::SetUp();
     89 
     90     ResetShorcutHelper();
     91   }
     92 
     93   virtual void TearDown() OVERRIDE {
     94     delete shortcut_helper_;
     95     shortcut_helper_ = NULL;
     96 
     97     ChromeRenderViewHostTestHarness::TearDown();
     98 
     99     gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, NULL);
    100   }
    101 
    102   GURL FindBestMatchingIcon(const std::vector<content::Manifest::Icon>& icons) {
    103     return shortcut_helper_->FindBestMatchingIcon(icons);
    104   }
    105 
    106   void SetDisplayDeviceScaleFactor(float device_scale_factor) {
    107     fake_screen_.SetDisplayDeviceScaleFactor(device_scale_factor);
    108 
    109     ResetShorcutHelper();
    110   }
    111 
    112   static int GetPreferredIconSizeInDp() {
    113     return ShortcutHelper::kPreferredIconSizeInDp;
    114   }
    115 
    116   static content::Manifest::Icon CreateIcon(
    117       const std::string& url,
    118       const std::string& type,
    119       double density,
    120       const std::vector<gfx::Size> sizes) {
    121     content::Manifest::Icon icon;
    122     icon.src = GURL(url);
    123     if (!type.empty())
    124       icon.type = base::NullableString16(base::UTF8ToUTF16(type), false);
    125     icon.density = density;
    126     icon.sizes = sizes;
    127 
    128     return icon;
    129   }
    130 
    131  private:
    132   ShortcutHelper* shortcut_helper_;
    133   FakeScreen fake_screen_;
    134 
    135   DISALLOW_COPY_AND_ASSIGN(ShortcutHelperTest);
    136 };
    137 
    138 TEST_F(ShortcutHelperTest, NoIcons) {
    139   // No icons should return the empty URL.
    140   std::vector<content::Manifest::Icon> icons;
    141   GURL url = FindBestMatchingIcon(icons);
    142   EXPECT_TRUE(url.is_empty());
    143 }
    144 
    145 TEST_F(ShortcutHelperTest, NoSizes) {
    146   // Icon with no sizes are ignored.
    147   std::vector<content::Manifest::Icon> icons;
    148   icons.push_back(
    149       CreateIcon("http://foo.com/icon.png", "", 1.0, std::vector<gfx::Size>()));
    150 
    151   GURL url = FindBestMatchingIcon(icons);
    152   EXPECT_TRUE(url.is_empty());
    153 }
    154 
    155 TEST_F(ShortcutHelperTest, MIMETypeFiltering) {
    156   // Icons with type specified to a MIME type that isn't a valid image MIME type
    157   // are ignored.
    158   std::vector<gfx::Size> sizes;
    159   sizes.push_back(gfx::Size(10, 10));
    160 
    161   std::vector<content::Manifest::Icon> icons;
    162   icons.push_back(
    163       CreateIcon("http://foo.com/icon.png", "image/foo_bar", 1.0, sizes));
    164   icons.push_back(CreateIcon("http://foo.com/icon.png", "image/", 1.0, sizes));
    165   icons.push_back(CreateIcon("http://foo.com/icon.png", "image/", 1.0, sizes));
    166   icons.push_back(
    167       CreateIcon("http://foo.com/icon.png", "video/mp4", 1.0, sizes));
    168 
    169   GURL url = FindBestMatchingIcon(icons);
    170   EXPECT_TRUE(url.is_empty());
    171 
    172   icons.clear();
    173   icons.push_back(
    174       CreateIcon("http://foo.com/icon.png", "image/png", 1.0, sizes));
    175   url = FindBestMatchingIcon(icons);
    176   EXPECT_EQ("http://foo.com/icon.png", url.spec());
    177 
    178   icons.clear();
    179   icons.push_back(
    180       CreateIcon("http://foo.com/icon.png", "image/gif", 1.0, sizes));
    181   url = FindBestMatchingIcon(icons);
    182   EXPECT_EQ("http://foo.com/icon.png", url.spec());
    183 
    184   icons.clear();
    185   icons.push_back(
    186       CreateIcon("http://foo.com/icon.png", "image/jpeg", 1.0, sizes));
    187   url = FindBestMatchingIcon(icons);
    188   EXPECT_EQ("http://foo.com/icon.png", url.spec());
    189 }
    190 
    191 TEST_F(ShortcutHelperTest, PreferredSizeOfCurrentDensityIsUsedFirst) {
    192   // This test has three icons each are marked with sizes set to the preferred
    193   // icon size for the associated density.
    194   std::vector<gfx::Size> sizes_1;
    195   sizes_1.push_back(gfx::Size(GetPreferredIconSizeInDp(),
    196                               GetPreferredIconSizeInDp()));
    197 
    198   std::vector<gfx::Size> sizes_2;
    199   sizes_2.push_back(gfx::Size(GetPreferredIconSizeInDp() * 2,
    200                               GetPreferredIconSizeInDp() * 2));
    201 
    202   std::vector<gfx::Size> sizes_3;
    203   sizes_3.push_back(gfx::Size(GetPreferredIconSizeInDp() * 3,
    204                               GetPreferredIconSizeInDp() * 3));
    205 
    206   std::vector<content::Manifest::Icon> icons;
    207   icons.push_back(CreateIcon("http://foo.com/icon_x1.png", "", 1.0, sizes_1));
    208   icons.push_back(CreateIcon("http://foo.com/icon_x2.png", "", 2.0, sizes_2));
    209   icons.push_back(CreateIcon("http://foo.com/icon_x3.png", "", 3.0, sizes_3));
    210 
    211   SetDisplayDeviceScaleFactor(1.0f);
    212   GURL url = FindBestMatchingIcon(icons);
    213   EXPECT_EQ("http://foo.com/icon_x1.png", url.spec());
    214 
    215   SetDisplayDeviceScaleFactor(2.0f);
    216   url = FindBestMatchingIcon(icons);
    217   EXPECT_EQ("http://foo.com/icon_x2.png", url.spec());
    218 
    219   SetDisplayDeviceScaleFactor(3.0f);
    220   url = FindBestMatchingIcon(icons);
    221   EXPECT_EQ("http://foo.com/icon_x3.png", url.spec());
    222 }
    223 
    224 TEST_F(ShortcutHelperTest, PreferredSizeOfDefaultDensityIsUsedSecond) {
    225   // This test has three icons. The first one is of density zero and is marked
    226   // with three sizes which are the preferred icon size for density 1, 2 and 3.
    227   // The icon for density 2 and 3 have a size set to 2x2 and 3x3.
    228   // Regardless of the device scale factor, the icon of density 1 is going to be
    229   // used because it matches the preferred size.
    230   std::vector<gfx::Size> sizes_1;
    231   sizes_1.push_back(gfx::Size(GetPreferredIconSizeInDp(),
    232                               GetPreferredIconSizeInDp()));
    233   sizes_1.push_back(gfx::Size(GetPreferredIconSizeInDp() * 2,
    234                               GetPreferredIconSizeInDp() * 2));
    235   sizes_1.push_back(gfx::Size(GetPreferredIconSizeInDp() * 3,
    236                               GetPreferredIconSizeInDp() * 3));
    237 
    238   std::vector<gfx::Size> sizes_2;
    239   sizes_2.push_back(gfx::Size(2, 2));
    240 
    241   std::vector<gfx::Size> sizes_3;
    242   sizes_3.push_back(gfx::Size(3, 3));
    243 
    244   std::vector<content::Manifest::Icon> icons;
    245   icons.push_back(CreateIcon("http://foo.com/icon_x1.png", "", 1.0, sizes_1));
    246   icons.push_back(CreateIcon("http://foo.com/icon_x2.png", "", 2.0, sizes_2));
    247   icons.push_back(CreateIcon("http://foo.com/icon_x3.png", "", 3.0, sizes_3));
    248 
    249   SetDisplayDeviceScaleFactor(1.0f);
    250   GURL url = FindBestMatchingIcon(icons);
    251   EXPECT_EQ("http://foo.com/icon_x1.png", url.spec());
    252 
    253   SetDisplayDeviceScaleFactor(2.0f);
    254   url = FindBestMatchingIcon(icons);
    255   EXPECT_EQ("http://foo.com/icon_x1.png", url.spec());
    256 
    257   SetDisplayDeviceScaleFactor(3.0f);
    258   url = FindBestMatchingIcon(icons);
    259   EXPECT_EQ("http://foo.com/icon_x1.png", url.spec());
    260 }
    261 
    262 TEST_F(ShortcutHelperTest, DeviceDensityFirst) {
    263   // If there is no perfect icon but an icon of the current device density is
    264   // present, it will be picked.
    265   // This test has three icons each are marked with sizes set to the preferred
    266   // icon size for the associated density.
    267   std::vector<gfx::Size> sizes;
    268   sizes.push_back(gfx::Size(2, 2));
    269 
    270   std::vector<content::Manifest::Icon> icons;
    271   icons.push_back(CreateIcon("http://foo.com/icon_x1.png", "", 1.0, sizes));
    272   icons.push_back(CreateIcon("http://foo.com/icon_x2.png", "", 2.0, sizes));
    273   icons.push_back(CreateIcon("http://foo.com/icon_x3.png", "", 3.0, sizes));
    274 
    275   SetDisplayDeviceScaleFactor(1.0f);
    276   GURL url = FindBestMatchingIcon(icons);
    277   EXPECT_EQ("http://foo.com/icon_x1.png", url.spec());
    278 
    279   SetDisplayDeviceScaleFactor(2.0f);
    280   url = FindBestMatchingIcon(icons);
    281   EXPECT_EQ("http://foo.com/icon_x2.png", url.spec());
    282 
    283   SetDisplayDeviceScaleFactor(3.0f);
    284   url = FindBestMatchingIcon(icons);
    285   EXPECT_EQ("http://foo.com/icon_x3.png", url.spec());
    286 }
    287 
    288 TEST_F(ShortcutHelperTest, DeviceDensityFallback) {
    289   // If there is no perfect icon but and no icon of the current display density,
    290   // an icon of density 1.0 will be used.
    291   std::vector<gfx::Size> sizes;
    292   sizes.push_back(gfx::Size(2, 2));
    293 
    294   std::vector<content::Manifest::Icon> icons;
    295   icons.push_back(CreateIcon("http://foo.com/icon_x1.png", "", 1.0, sizes));
    296   icons.push_back(CreateIcon("http://foo.com/icon_x2.png", "", 2.0, sizes));
    297 
    298   SetDisplayDeviceScaleFactor(3.0f);
    299   GURL url = FindBestMatchingIcon(icons);
    300   EXPECT_EQ("http://foo.com/icon_x1.png", url.spec());
    301 }
    302 
    303 TEST_F(ShortcutHelperTest, DoNotUseOtherDensities) {
    304   // If there are only icons of densities that are not the current display
    305   // density or the default density, they are ignored.
    306   std::vector<gfx::Size> sizes;
    307   sizes.push_back(gfx::Size(2, 2));
    308 
    309   std::vector<content::Manifest::Icon> icons;
    310   icons.push_back(CreateIcon("http://foo.com/icon_x2.png", "", 2.0, sizes));
    311 
    312   SetDisplayDeviceScaleFactor(3.0f);
    313   GURL url = FindBestMatchingIcon(icons);
    314   EXPECT_TRUE(url.is_empty());
    315 }
    316 
    317 TEST_F(ShortcutHelperTest, NotSquareIconsAreIgnored) {
    318   std::vector<gfx::Size> sizes;
    319   sizes.push_back(gfx::Size(20, 2));
    320 
    321   std::vector<content::Manifest::Icon> icons;
    322   icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes));
    323 
    324   GURL url = FindBestMatchingIcon(icons);
    325   EXPECT_TRUE(url.is_empty());
    326 }
    327 
    328 TEST_F(ShortcutHelperTest, ClosestIconToPreferred) {
    329   // This test verifies ShortcutHelper::FindBestMatchingIcon by passing
    330   // different icon sizes and checking which one is picked.
    331   // The Device Scale Factor is 1.0 and the preferred icon size is returned by
    332   // GetPreferredIconSizeInDp().
    333   int very_small = GetPreferredIconSizeInDp() / 4;
    334   int small = GetPreferredIconSizeInDp() / 2;
    335   int bit_small = GetPreferredIconSizeInDp() - 1;
    336   int bit_big = GetPreferredIconSizeInDp() + 1;
    337   int big = GetPreferredIconSizeInDp() * 2;
    338   int very_big = GetPreferredIconSizeInDp() * 4;
    339 
    340   // (very_small, bit_small) => bit_small
    341   {
    342     std::vector<gfx::Size> sizes_1;
    343     sizes_1.push_back(gfx::Size(very_small, very_small));
    344 
    345     std::vector<gfx::Size> sizes_2;
    346     sizes_2.push_back(gfx::Size(bit_small, bit_small));
    347 
    348     std::vector<content::Manifest::Icon> icons;
    349     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    350     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_2));
    351 
    352     GURL url = FindBestMatchingIcon(icons);
    353     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    354   }
    355 
    356   // (very_small, bit_small, smaller) => bit_small
    357   {
    358     std::vector<gfx::Size> sizes_1;
    359     sizes_1.push_back(gfx::Size(very_small, very_small));
    360 
    361     std::vector<gfx::Size> sizes_2;
    362     sizes_2.push_back(gfx::Size(bit_small, bit_small));
    363 
    364     std::vector<gfx::Size> sizes_3;
    365     sizes_3.push_back(gfx::Size(small, small));
    366 
    367     std::vector<content::Manifest::Icon> icons;
    368     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    369     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_2));
    370     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_3));
    371 
    372     GURL url = FindBestMatchingIcon(icons);
    373     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    374   }
    375 
    376   // (very_big, big) => big
    377   {
    378     std::vector<gfx::Size> sizes_1;
    379     sizes_1.push_back(gfx::Size(very_big, very_big));
    380 
    381     std::vector<gfx::Size> sizes_2;
    382     sizes_2.push_back(gfx::Size(big, big));
    383 
    384     std::vector<content::Manifest::Icon> icons;
    385     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    386     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_2));
    387 
    388     GURL url = FindBestMatchingIcon(icons);
    389     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    390   }
    391 
    392   // (very_big, big, bit_big) => bit_big
    393   {
    394     std::vector<gfx::Size> sizes_1;
    395     sizes_1.push_back(gfx::Size(very_big, very_big));
    396 
    397     std::vector<gfx::Size> sizes_2;
    398     sizes_2.push_back(gfx::Size(big, big));
    399 
    400     std::vector<gfx::Size> sizes_3;
    401     sizes_3.push_back(gfx::Size(bit_big, bit_big));
    402 
    403     std::vector<content::Manifest::Icon> icons;
    404     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    405     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_2));
    406     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_3));
    407 
    408     GURL url = FindBestMatchingIcon(icons);
    409     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    410   }
    411 
    412   // (bit_small, very_big) => very_big
    413   {
    414     std::vector<gfx::Size> sizes_1;
    415     sizes_1.push_back(gfx::Size(bit_small, bit_small));
    416 
    417     std::vector<gfx::Size> sizes_2;
    418     sizes_2.push_back(gfx::Size(very_big, very_big));
    419 
    420     std::vector<content::Manifest::Icon> icons;
    421     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    422     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_2));
    423 
    424     GURL url = FindBestMatchingIcon(icons);
    425     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    426   }
    427 
    428   // (bit_small, bit_big) => bit_big
    429   {
    430     std::vector<gfx::Size> sizes_1;
    431     sizes_1.push_back(gfx::Size(bit_small, bit_small));
    432 
    433     std::vector<gfx::Size> sizes_2;
    434     sizes_2.push_back(gfx::Size(bit_big, bit_big));
    435 
    436     std::vector<content::Manifest::Icon> icons;
    437     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    438     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_2));
    439 
    440     GURL url = FindBestMatchingIcon(icons);
    441     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    442   }
    443 }
    444 
    445 TEST_F(ShortcutHelperTest, UseAnyIfNoPreferredSize) {
    446   // 'any' (ie. gfx::Size(0,0)) should be used if there is no icon of a
    447   // preferred size. An icon with the current device scale factor is preferred
    448   // over one with the default density.
    449 
    450   // 'any' with preferred size => preferred size
    451   {
    452     std::vector<gfx::Size> sizes_1;
    453     sizes_1.push_back(gfx::Size(GetPreferredIconSizeInDp(),
    454                                 GetPreferredIconSizeInDp()));
    455     std::vector<gfx::Size> sizes_2;
    456     sizes_2.push_back(gfx::Size(0,0));
    457 
    458     std::vector<content::Manifest::Icon> icons;
    459     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_1));
    460     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_2));
    461 
    462     GURL url = FindBestMatchingIcon(icons);
    463     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    464   }
    465 
    466   // 'any' with nearly preferred size => any
    467   {
    468     std::vector<gfx::Size> sizes_1;
    469     sizes_1.push_back(gfx::Size(GetPreferredIconSizeInDp() + 1,
    470                                 GetPreferredIconSizeInDp() + 1));
    471     std::vector<gfx::Size> sizes_2;
    472     sizes_2.push_back(gfx::Size(0,0));
    473 
    474     std::vector<content::Manifest::Icon> icons;
    475     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes_1));
    476     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 1.0, sizes_2));
    477 
    478     GURL url = FindBestMatchingIcon(icons);
    479     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    480   }
    481 
    482   // 'any' on default density and current density => current density.
    483   {
    484     std::vector<gfx::Size> sizes;
    485     sizes.push_back(gfx::Size(0,0));
    486 
    487     std::vector<content::Manifest::Icon> icons;
    488     icons.push_back(CreateIcon("http://foo.com/icon_no.png", "", 1.0, sizes));
    489     icons.push_back(CreateIcon("http://foo.com/icon.png", "", 3.0, sizes));
    490 
    491     SetDisplayDeviceScaleFactor(3.0f);
    492     GURL url = FindBestMatchingIcon(icons);
    493     EXPECT_EQ("http://foo.com/icon.png", url.spec());
    494   }
    495 }
    496