Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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/common/extensions/manifest.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 #include <string>
     10 
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/values.h"
     14 #include "chrome/common/extensions/extension_manifest_constants.h"
     15 #include "chrome/common/extensions/features/feature.h"
     16 #include "chrome/common/extensions/features/simple_feature.h"
     17 #include "extensions/common/error_utils.h"
     18 #include "extensions/common/install_warning.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace errors = extension_manifest_errors;
     22 namespace keys = extension_manifest_keys;
     23 
     24 namespace extensions {
     25 
     26 class ManifestTest : public testing::Test {
     27  public:
     28   ManifestTest() : default_value_("test") {}
     29 
     30  protected:
     31   void AssertType(Manifest* manifest, Manifest::Type type) {
     32     EXPECT_EQ(type, manifest->type());
     33     EXPECT_EQ(type == Manifest::TYPE_THEME, manifest->is_theme());
     34     EXPECT_EQ(type == Manifest::TYPE_PLATFORM_APP,
     35               manifest->is_platform_app());
     36     EXPECT_EQ(type == Manifest::TYPE_LEGACY_PACKAGED_APP,
     37               manifest->is_legacy_packaged_app());
     38     EXPECT_EQ(type == Manifest::TYPE_HOSTED_APP, manifest->is_hosted_app());
     39     EXPECT_EQ(type == Manifest::TYPE_SHARED_MODULE,
     40               manifest->is_shared_module());
     41   }
     42 
     43   // Helper function that replaces the Manifest held by |manifest| with a copy
     44   // with its |key| changed to |value|. If |value| is NULL, then |key| will
     45   // instead be deleted.
     46   void MutateManifest(scoped_ptr<Manifest>* manifest,
     47                       const std::string& key,
     48                       base::Value* value) {
     49     scoped_ptr<base::DictionaryValue> manifest_value(
     50         manifest->get()->value()->DeepCopy());
     51     if (value)
     52       manifest_value->Set(key, value);
     53     else
     54       manifest_value->Remove(key, NULL);
     55     manifest->reset(new Manifest(Manifest::INTERNAL, manifest_value.Pass()));
     56   }
     57 
     58   std::string default_value_;
     59 };
     60 
     61 // Verifies that extensions can access the correct keys.
     62 TEST_F(ManifestTest, Extension) {
     63   scoped_ptr<base::DictionaryValue> manifest_value(new base::DictionaryValue());
     64   manifest_value->SetString(keys::kName, "extension");
     65   manifest_value->SetString(keys::kVersion, "1");
     66   // Only supported in manifest_version=1.
     67   manifest_value->SetString(keys::kBackgroundPageLegacy, "bg.html");
     68   manifest_value->SetString("unknown_key", "foo");
     69 
     70   scoped_ptr<Manifest> manifest(
     71       new Manifest(Manifest::INTERNAL, manifest_value.Pass()));
     72   std::string error;
     73   std::vector<InstallWarning> warnings;
     74   EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
     75   EXPECT_TRUE(error.empty());
     76   ASSERT_EQ(1u, warnings.size());
     77   AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
     78 
     79   // The known key 'background_page' should be accessible.
     80   std::string value;
     81   EXPECT_TRUE(manifest->GetString(keys::kBackgroundPageLegacy, &value));
     82   EXPECT_EQ("bg.html", value);
     83 
     84   // The unknown key 'unknown_key' should be accesible.
     85   value.clear();
     86   EXPECT_TRUE(manifest->GetString("unknown_key", &value));
     87   EXPECT_EQ("foo", value);
     88 
     89   // Set the manifest_version to 2; background_page should stop working.
     90   value.clear();
     91   MutateManifest(
     92       &manifest, keys::kManifestVersion, new base::FundamentalValue(2));
     93   EXPECT_FALSE(manifest->GetString("background_page", &value));
     94   EXPECT_EQ("", value);
     95 
     96   // Validate should also give a warning.
     97   warnings.clear();
     98   EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
     99   EXPECT_TRUE(error.empty());
    100   ASSERT_EQ(2u, warnings.size());
    101   {
    102     SimpleFeature feature;
    103     feature.set_name("background_page");
    104     feature.set_max_manifest_version(1);
    105     EXPECT_EQ(
    106         "'background_page' requires manifest version of 1 or lower.",
    107         warnings[0].message);
    108   }
    109 
    110   // Test DeepCopy and Equals.
    111   scoped_ptr<Manifest> manifest2(manifest->DeepCopy());
    112   EXPECT_TRUE(manifest->Equals(manifest2.get()));
    113   EXPECT_TRUE(manifest2->Equals(manifest.get()));
    114   MutateManifest(
    115       &manifest, "foo", new base::StringValue("blah"));
    116   EXPECT_FALSE(manifest->Equals(manifest2.get()));
    117 }
    118 
    119 // Verifies that key restriction based on type works.
    120 TEST_F(ManifestTest, ExtensionTypes) {
    121   scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
    122   value->SetString(keys::kName, "extension");
    123   value->SetString(keys::kVersion, "1");
    124 
    125   scoped_ptr<Manifest> manifest(
    126       new Manifest(Manifest::INTERNAL, value.Pass()));
    127   std::string error;
    128   std::vector<InstallWarning> warnings;
    129   EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
    130   EXPECT_TRUE(error.empty());
    131   EXPECT_TRUE(warnings.empty());
    132 
    133   // By default, the type is Extension.
    134   AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
    135 
    136   // Theme.
    137   MutateManifest(
    138       &manifest, keys::kTheme, new base::DictionaryValue());
    139   AssertType(manifest.get(), Manifest::TYPE_THEME);
    140   MutateManifest(
    141       &manifest, keys::kTheme, NULL);
    142 
    143   // Shared module.
    144   MutateManifest(
    145       &manifest, keys::kExport, new base::DictionaryValue());
    146   AssertType(manifest.get(), Manifest::TYPE_SHARED_MODULE);
    147   MutateManifest(
    148       &manifest, keys::kExport, NULL);
    149 
    150   // Packaged app.
    151   MutateManifest(
    152       &manifest, keys::kApp, new base::DictionaryValue());
    153   AssertType(manifest.get(), Manifest::TYPE_LEGACY_PACKAGED_APP);
    154 
    155   // Platform app.
    156   MutateManifest(
    157       &manifest, keys::kPlatformAppBackground, new base::DictionaryValue());
    158   AssertType(manifest.get(), Manifest::TYPE_PLATFORM_APP);
    159   MutateManifest(
    160       &manifest, keys::kPlatformAppBackground, NULL);
    161 
    162   // Hosted app.
    163   MutateManifest(
    164       &manifest, keys::kWebURLs, new base::ListValue());
    165   AssertType(manifest.get(), Manifest::TYPE_HOSTED_APP);
    166   MutateManifest(
    167       &manifest, keys::kWebURLs, NULL);
    168   MutateManifest(
    169       &manifest, keys::kLaunchWebURL, new base::StringValue("foo"));
    170   AssertType(manifest.get(), Manifest::TYPE_HOSTED_APP);
    171   MutateManifest(
    172       &manifest, keys::kLaunchWebURL, NULL);
    173 };
    174 
    175 // Verifies that the getters filter restricted keys.
    176 TEST_F(ManifestTest, RestrictedKeys) {
    177   scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
    178   value->SetString(keys::kName, "extension");
    179   value->SetString(keys::kVersion, "1");
    180 
    181   scoped_ptr<Manifest> manifest(
    182       new Manifest(Manifest::INTERNAL, value.Pass()));
    183   std::string error;
    184   std::vector<InstallWarning> warnings;
    185   EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
    186   EXPECT_TRUE(error.empty());
    187   EXPECT_TRUE(warnings.empty());
    188 
    189   // "Commands" requires manifest version 2.
    190   const base::Value* output = NULL;
    191   MutateManifest(
    192       &manifest, keys::kCommands, new base::DictionaryValue());
    193   EXPECT_FALSE(manifest->HasKey(keys::kCommands));
    194   EXPECT_FALSE(manifest->Get(keys::kCommands, &output));
    195 
    196   MutateManifest(
    197       &manifest, keys::kManifestVersion, new base::FundamentalValue(2));
    198   EXPECT_TRUE(manifest->HasKey(keys::kCommands));
    199   EXPECT_TRUE(manifest->Get(keys::kCommands, &output));
    200 
    201   MutateManifest(
    202       &manifest, keys::kPageAction, new base::DictionaryValue());
    203   AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
    204   EXPECT_TRUE(manifest->HasKey(keys::kPageAction));
    205   EXPECT_TRUE(manifest->Get(keys::kPageAction, &output));
    206 
    207   // Platform apps cannot have a "page_action" key.
    208   MutateManifest(
    209       &manifest, keys::kPlatformAppBackground, new base::DictionaryValue());
    210   AssertType(manifest.get(), Manifest::TYPE_PLATFORM_APP);
    211   EXPECT_FALSE(manifest->HasKey(keys::kPageAction));
    212   EXPECT_FALSE(manifest->Get(keys::kPageAction, &output));
    213   MutateManifest(
    214       &manifest, keys::kPlatformAppBackground, NULL);
    215 
    216   // Platform apps also can't have a "Commands" key.
    217   EXPECT_FALSE(manifest->HasKey(keys::kCommands));
    218   EXPECT_FALSE(manifest->Get(keys::kCommands, &output));
    219 };
    220 
    221 }  // namespace extensions
    222