Home | History | Annotate | Download | only in extensions
      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/extensions/extension_action_manager.h"
      6 
      7 #include "base/strings/string_number_conversions.h"
      8 #include "chrome/browser/extensions/extension_action.h"
      9 #include "chrome/test/base/testing_profile.h"
     10 #include "extensions/browser/extension_registry.h"
     11 #include "extensions/common/extension_builder.h"
     12 #include "extensions/common/manifest_handlers/icons_handler.h"
     13 #include "extensions/common/value_builder.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 namespace extensions {
     17 
     18 namespace {
     19 
     20 const char kBrowserAction[] = "browser_action";
     21 const char kPageAction[] = "page_action";
     22 
     23 }  // namespace
     24 
     25 class ExtensionActionManagerTest : public testing::Test {
     26  public:
     27   ExtensionActionManagerTest();
     28 
     29  protected:
     30   // Build an extension, populating |action_type| key with |action|, and
     31   // "icons" key with |extension_icons|.
     32   scoped_refptr<Extension> BuildExtension(DictionaryBuilder& extension_icons,
     33                                           DictionaryBuilder& action,
     34                                           const char* action_type);
     35 
     36   // Returns true if |action|'s title matches |extension|'s name.
     37   bool TitlesMatch(const Extension& extension, const ExtensionAction& action);
     38 
     39   // Returns true if |action|'s icon for size |action_key| matches
     40   // |extension|'s icon for size |extension_key|;
     41   bool IconsMatch(const Extension& extension,
     42                   int extension_key,
     43                   const ExtensionAction& action,
     44                   int action_key);
     45 
     46   // Returns the appropriate action for |extension| according to |action_type|.
     47   ExtensionAction* GetAction(const char* action_type,
     48                              const Extension& extension);
     49 
     50   // Tests that values that are missing from the |action_type| key are properly
     51   // populated with values from the other keys in the manifest (e.g.
     52   // "default_icon" key of |action_type| is populated with "icons" key).
     53   void TestPopulateMissingValues(const char* action_type);
     54 
     55   ExtensionActionManager* manager() { return manager_; }
     56 
     57  private:
     58   ExtensionRegistry* registry_;
     59   int curr_id_;
     60   ExtensionActionManager* manager_;
     61   scoped_ptr<TestingProfile> profile_;
     62 };
     63 
     64 ExtensionActionManagerTest::ExtensionActionManagerTest()
     65     : curr_id_(0),
     66       profile_(new TestingProfile) {
     67   registry_ = ExtensionRegistry::Get(profile_.get());
     68   manager_ = ExtensionActionManager::Get(profile_.get());
     69 }
     70 
     71 scoped_refptr<Extension> ExtensionActionManagerTest::BuildExtension(
     72     DictionaryBuilder& extension_icons,
     73     DictionaryBuilder& action,
     74     const char* action_type) {
     75   std::string id = base::IntToString(curr_id_++);
     76   scoped_refptr<Extension> extension = ExtensionBuilder()
     77       .SetManifest(DictionaryBuilder().Set("version", "1")
     78           .Set("manifest_version", 2)
     79           .Set("icons", extension_icons)
     80           .Set(action_type, action)
     81           .Set("name",
     82               std::string("Test Extension").append(id)))
     83       .SetID(id)
     84       .Build();
     85   registry_->AddEnabled(extension);
     86   return extension;
     87 }
     88 
     89 bool ExtensionActionManagerTest::TitlesMatch(const Extension& extension,
     90                                              const ExtensionAction& action) {
     91   return action.GetTitle(ExtensionAction::kDefaultTabId) == extension.name();
     92 }
     93 
     94 bool ExtensionActionManagerTest::IconsMatch(const Extension& extension,
     95                                             int extension_key,
     96                                             const ExtensionAction& action,
     97                                             int action_key) {
     98   return action.default_icon()->Get(action_key,
     99                                     ExtensionIconSet::MATCH_EXACTLY) ==
    100       IconsInfo::GetIcons(&extension).Get(extension_key,
    101                                           ExtensionIconSet::MATCH_EXACTLY);
    102 }
    103 
    104 ExtensionAction* ExtensionActionManagerTest::GetAction(
    105     const char* action_type,
    106     const Extension& extension) {
    107   return (action_type == kBrowserAction) ?
    108       manager_->GetBrowserAction(extension) :
    109       manager_->GetPageAction(extension);
    110 }
    111 
    112 void ExtensionActionManagerTest::TestPopulateMissingValues(
    113     const char* action_type) {
    114   // Test that the largest icon from the extension's "icons" key is chosen as a
    115   // replacement for missing action default_icons keys. "19" should not be
    116   // replaced because "38" can always be used in its place.
    117   scoped_refptr<Extension> extension = BuildExtension(
    118       DictionaryBuilder().Set("48", "icon48.png")
    119                          .Set("128", "icon128.png"),
    120       DictionaryBuilder().Pass(),
    121       action_type);
    122 
    123   ASSERT_TRUE(extension.get());
    124   const ExtensionAction* action = GetAction(action_type, *extension.get());
    125   ASSERT_TRUE(action);
    126 
    127   ASSERT_TRUE(TitlesMatch(*extension.get(), *action));
    128   ASSERT_TRUE(IconsMatch(*extension.get(), 128, *action, 38));
    129 
    130   // Test that the action's missing default_icons are not replaced with smaller
    131   // icons.
    132   extension = BuildExtension(
    133       DictionaryBuilder().Set("24", "icon24.png"),
    134       DictionaryBuilder().Pass(),
    135       action_type);
    136 
    137   ASSERT_TRUE(extension.get());
    138   action = GetAction(action_type, *extension.get());
    139   ASSERT_TRUE(action);
    140 
    141   ASSERT_TRUE(IconsMatch(*extension.get(), 24, *action, 19));
    142   ASSERT_FALSE(IconsMatch(*extension.get(), 24, *action, 38));
    143 
    144   // Test that an action's 19px icon is not replaced if a 38px action icon
    145   // exists.
    146   extension = BuildExtension(
    147       DictionaryBuilder().Set("128", "icon128.png"),
    148       DictionaryBuilder().Set("default_icon", DictionaryBuilder()
    149                                               .Set("38", "action38.png")),
    150       action_type);
    151 
    152   ASSERT_TRUE(extension.get());
    153   action = GetAction(action_type, *extension.get());
    154   ASSERT_TRUE(action);
    155 
    156   ASSERT_FALSE(IconsMatch(*extension.get(), 128, *action, 19));
    157 
    158   // Test that existing default_icons and default_title are not replaced.
    159   extension = BuildExtension(
    160       DictionaryBuilder().Set("128", "icon128.png"),
    161       DictionaryBuilder().Set("default_title", "Action!")
    162                          .Set("default_icon", DictionaryBuilder()
    163                                               .Set("19", "action19.png")
    164                                               .Set("38", "action38.png")),
    165       action_type);
    166 
    167   ASSERT_TRUE(extension.get());
    168   action = GetAction(action_type, *extension.get());
    169   ASSERT_TRUE(action);
    170 
    171   ASSERT_FALSE(TitlesMatch(*extension.get(), *action));
    172   ASSERT_FALSE(IconsMatch(*extension.get(), 128, *action, 19));
    173   ASSERT_FALSE(IconsMatch(*extension.get(), 128, *action, 38));
    174 }
    175 
    176 namespace {
    177 
    178 TEST_F(ExtensionActionManagerTest, PopulateBrowserAction) {
    179   TestPopulateMissingValues(kBrowserAction);
    180 }
    181 
    182 TEST_F(ExtensionActionManagerTest, PopulatePageAction) {
    183   TestPopulateMissingValues(kPageAction);
    184 }
    185 
    186 TEST_F(ExtensionActionManagerTest, GetBestFitActionTest) {
    187   // Create an extension with page action defaults.
    188   scoped_refptr<Extension> extension = BuildExtension(
    189       DictionaryBuilder().Set("48", "icon48.png"),
    190       DictionaryBuilder().Set("default_title", "Action!")
    191                          .Set("default_icon", DictionaryBuilder()
    192                                               .Set("38", "action38.png")),
    193       kPageAction);
    194   ASSERT_TRUE(extension.get());
    195 
    196   // Get a "best fit" browser action for |extension|.
    197   scoped_ptr<ExtensionAction> action =
    198       manager()->GetBestFitAction(*extension.get(), ActionInfo::TYPE_BROWSER);
    199   ASSERT_TRUE(action.get());
    200   ASSERT_EQ(action->action_type(), ActionInfo::TYPE_BROWSER);
    201 
    202   // |action|'s title and default icon should match |extension|'s page action's.
    203   ASSERT_EQ(action->GetTitle(ExtensionAction::kDefaultTabId), "Action!");
    204   ASSERT_EQ(action->default_icon()->Get(38, ExtensionIconSet::MATCH_EXACTLY),
    205             "action38.png");
    206 
    207   // Create a new extension without page action defaults.
    208   extension = BuildExtension(
    209       DictionaryBuilder().Set("48", "icon48.png"),
    210       DictionaryBuilder().Pass(),
    211       kPageAction);
    212   ASSERT_TRUE(extension.get());
    213 
    214   action =
    215       manager()->GetBestFitAction(*extension.get(), ActionInfo::TYPE_BROWSER);
    216 
    217   // Now these values match because |extension| does not have page action
    218   // defaults.
    219   ASSERT_TRUE(TitlesMatch(*extension.get(), *action));
    220   ASSERT_TRUE(IconsMatch(*extension.get(), 48, *action, 38));
    221 }
    222 
    223 }  // namespace
    224 }  // namespace extensions
    225