extension_manifests_unittest.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 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 "base/command_line.h" 6#include "base/file_path.h" 7#include "base/file_util.h" 8#include "base/path_service.h" 9#include "base/scoped_ptr.h" 10#include "base/string_util.h" 11#include "chrome/common/chrome_paths.h" 12#include "chrome/common/chrome_switches.h" 13#include "chrome/common/extensions/extension.h" 14#include "chrome/common/extensions/extension_constants.h" 15#include "chrome/common/extensions/extension_error_utils.h" 16#include "chrome/common/json_value_serializer.h" 17#include "testing/gtest/include/gtest/gtest.h" 18 19namespace errors = extension_manifest_errors; 20namespace keys = extension_manifest_keys; 21 22class ExtensionManifestTest : public testing::Test { 23 public: 24 ExtensionManifestTest() : enable_apps_(true) {} 25 26 protected: 27 DictionaryValue* LoadManifestFile(const std::string& filename, 28 std::string* error) { 29 FilePath path; 30 PathService::Get(chrome::DIR_TEST_DATA, &path); 31 path = path.AppendASCII("extensions") 32 .AppendASCII("manifest_tests") 33 .AppendASCII(filename.c_str()); 34 EXPECT_TRUE(file_util::PathExists(path)); 35 36 JSONFileValueSerializer serializer(path); 37 return static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error)); 38 } 39 40 Extension* LoadExtensionWithLocation(DictionaryValue* value, 41 Extension::Location location, 42 std::string* error) { 43 FilePath path; 44 PathService::Get(chrome::DIR_TEST_DATA, &path); 45 path = path.AppendASCII("extensions").AppendASCII("manifest_tests"); 46 47 scoped_ptr<Extension> extension(new Extension(path.DirName())); 48 extension->set_location(location); 49 50 if (!extension->InitFromValue(*value, false, error)) 51 return NULL; 52 53 return extension.release(); 54 } 55 56 Extension* LoadExtension(const std::string& name, 57 std::string* error) { 58 return LoadExtensionWithLocation(name, Extension::INTERNAL, error); 59 } 60 61 Extension* LoadExtension(DictionaryValue* value, 62 std::string* error) { 63 return LoadExtensionWithLocation(value, Extension::INTERNAL, error); 64 } 65 66 Extension* LoadExtensionWithLocation(const std::string& name, 67 Extension::Location location, 68 std::string* error) { 69 scoped_ptr<DictionaryValue> value(LoadManifestFile(name, error)); 70 if (!value.get()) 71 return NULL; 72 return LoadExtensionWithLocation(value.get(), location, error); 73 } 74 75 Extension* LoadAndExpectSuccess(const std::string& name) { 76 std::string error; 77 Extension* extension = LoadExtension(name, &error); 78 EXPECT_TRUE(extension) << name; 79 EXPECT_EQ("", error) << name; 80 return extension; 81 } 82 83 Extension* LoadAndExpectSuccess(DictionaryValue* manifest, 84 const std::string& name) { 85 std::string error; 86 Extension* extension = LoadExtension(manifest, &error); 87 EXPECT_TRUE(extension) << "Unexpected success for " << name; 88 EXPECT_EQ("", error) << "Unexpected no error for " << name; 89 return extension; 90 } 91 92 void VerifyExpectedError(Extension* extension, 93 const std::string& name, 94 const std::string& error, 95 const std::string& expected_error) { 96 EXPECT_FALSE(extension) << 97 "Expected failure loading extension '" << name << 98 "', but didn't get one."; 99 EXPECT_TRUE(MatchPattern(error, expected_error)) << name << 100 " expected '" << expected_error << "' but got '" << error << "'"; 101 } 102 103 void LoadAndExpectError(const std::string& name, 104 const std::string& expected_error) { 105 std::string error; 106 scoped_ptr<Extension> extension(LoadExtension(name, &error)); 107 VerifyExpectedError(extension.get(), name, error, expected_error); 108 } 109 110 void LoadAndExpectError(DictionaryValue* manifest, 111 const std::string& name, 112 const std::string& expected_error) { 113 std::string error; 114 scoped_ptr<Extension> extension(LoadExtension(manifest, &error)); 115 VerifyExpectedError(extension.get(), name, error, expected_error); 116 } 117 118 bool enable_apps_; 119}; 120 121TEST_F(ExtensionManifestTest, ValidApp) { 122 scoped_ptr<Extension> extension(LoadAndExpectSuccess("valid_app.json")); 123 ASSERT_EQ(2u, extension->web_extent().patterns().size()); 124 EXPECT_EQ("http://www.google.com/mail/*", 125 extension->web_extent().patterns()[0].GetAsString()); 126 EXPECT_EQ("http://www.google.com/foobar/*", 127 extension->web_extent().patterns()[1].GetAsString()); 128 EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); 129 EXPECT_EQ("http://www.google.com/mail/", extension->launch_web_url()); 130} 131 132TEST_F(ExtensionManifestTest, AppWebUrls) { 133 LoadAndExpectError("web_urls_wrong_type.json", 134 errors::kInvalidWebURLs); 135 LoadAndExpectError("web_urls_invalid_1.json", 136 ExtensionErrorUtils::FormatErrorMessage( 137 errors::kInvalidWebURL, "0")); 138 LoadAndExpectError("web_urls_invalid_2.json", 139 ExtensionErrorUtils::FormatErrorMessage( 140 errors::kInvalidWebURL, "0")); 141 LoadAndExpectError("web_urls_invalid_3.json", 142 ExtensionErrorUtils::FormatErrorMessage( 143 errors::kInvalidWebURL, "0")); 144 LoadAndExpectError("web_urls_invalid_4.json", 145 ExtensionErrorUtils::FormatErrorMessage( 146 errors::kInvalidWebURL, "0")); 147 148 scoped_ptr<Extension> extension( 149 LoadAndExpectSuccess("web_urls_default.json")); 150 ASSERT_EQ(1u, extension->web_extent().patterns().size()); 151 EXPECT_EQ("*://www.google.com/*", 152 extension->web_extent().patterns()[0].GetAsString()); 153} 154 155TEST_F(ExtensionManifestTest, AppLaunchContainer) { 156 scoped_ptr<Extension> extension; 157 158 extension.reset(LoadAndExpectSuccess("launch_tab.json")); 159 EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); 160 161 extension.reset(LoadAndExpectSuccess("launch_panel.json")); 162 EXPECT_EQ(extension_misc::LAUNCH_PANEL, extension->launch_container()); 163 164 extension.reset(LoadAndExpectSuccess("launch_default.json")); 165 EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); 166 167 extension.reset(LoadAndExpectSuccess("launch_width.json")); 168 EXPECT_EQ(640, extension->launch_width()); 169 170 extension.reset(LoadAndExpectSuccess("launch_height.json")); 171 EXPECT_EQ(480, extension->launch_height()); 172 173 LoadAndExpectError("launch_window.json", 174 errors::kInvalidLaunchContainer); 175 LoadAndExpectError("launch_container_invalid_type.json", 176 errors::kInvalidLaunchContainer); 177 LoadAndExpectError("launch_container_invalid_value.json", 178 errors::kInvalidLaunchContainer); 179 LoadAndExpectError("launch_container_without_launch_url.json", 180 errors::kLaunchURLRequired); 181 LoadAndExpectError("launch_width_invalid.json", 182 errors::kInvalidLaunchWidthContainer); 183 LoadAndExpectError("launch_width_negative.json", 184 errors::kInvalidLaunchWidth); 185 LoadAndExpectError("launch_height_invalid.json", 186 errors::kInvalidLaunchHeightContainer); 187 LoadAndExpectError("launch_height_negative.json", 188 errors::kInvalidLaunchHeight); 189} 190 191TEST_F(ExtensionManifestTest, AppLaunchURL) { 192 LoadAndExpectError("launch_path_and_url.json", 193 errors::kLaunchPathAndURLAreExclusive); 194 LoadAndExpectError("launch_path_invalid_type.json", 195 errors::kInvalidLaunchLocalPath); 196 LoadAndExpectError("launch_path_invalid_value.json", 197 errors::kInvalidLaunchLocalPath); 198 LoadAndExpectError("launch_url_invalid_type.json", 199 errors::kInvalidLaunchWebURL); 200 201 scoped_ptr<Extension> extension; 202 extension.reset(LoadAndExpectSuccess("launch_local_path.json")); 203 EXPECT_EQ(extension->url().spec() + "launch.html", 204 extension->GetFullLaunchURL().spec()); 205 206 LoadAndExpectError("launch_web_url_relative.json", 207 errors::kInvalidLaunchWebURL); 208 209 extension.reset(LoadAndExpectSuccess("launch_web_url_absolute.json")); 210 EXPECT_EQ(GURL("http://www.google.com/launch.html"), 211 extension->GetFullLaunchURL()); 212} 213 214TEST_F(ExtensionManifestTest, Override) { 215 LoadAndExpectError("override_newtab_and_history.json", 216 errors::kMultipleOverrides); 217 LoadAndExpectError("override_invalid_page.json", 218 errors::kInvalidChromeURLOverrides); 219 220 scoped_ptr<Extension> extension; 221 222 extension.reset(LoadAndExpectSuccess("override_new_tab.json")); 223 EXPECT_EQ(extension->url().spec() + "newtab.html", 224 extension->GetChromeURLOverrides().find("newtab")->second.spec()); 225 226 extension.reset(LoadAndExpectSuccess("override_history.json")); 227 EXPECT_EQ(extension->url().spec() + "history.html", 228 extension->GetChromeURLOverrides().find("history")->second.spec()); 229} 230 231TEST_F(ExtensionManifestTest, ChromeURLPermissionInvalid) { 232 LoadAndExpectError("permission_chrome_url_invalid.json", 233 errors::kInvalidPermissionScheme); 234} 235 236TEST_F(ExtensionManifestTest, ChromeResourcesPermissionValidOnlyForComponents) { 237 LoadAndExpectError("permission_chrome_resources_url.json", 238 errors::kInvalidPermissionScheme); 239 std::string error; 240 scoped_ptr<Extension> extension; 241 extension.reset(LoadExtensionWithLocation( 242 "permission_chrome_resources_url.json", 243 Extension::COMPONENT, 244 &error)); 245 EXPECT_EQ("", error); 246} 247 248TEST_F(ExtensionManifestTest, ChromeURLContentScriptInvalid) { 249 LoadAndExpectError("content_script_chrome_url_invalid.json", 250 errors::kInvalidMatch); 251} 252 253TEST_F(ExtensionManifestTest, DevToolsExtensions) { 254 LoadAndExpectError("devtools_extension_no_permissions.json", 255 errors::kDevToolsExperimental); 256 LoadAndExpectError("devtools_extension_url_invalid_type.json", 257 errors::kInvalidDevToolsPage); 258 259 CommandLine old_command_line = *CommandLine::ForCurrentProcess(); 260 CommandLine::ForCurrentProcess()->AppendSwitch( 261 switches::kEnableExperimentalExtensionApis); 262 263 scoped_ptr<Extension> extension; 264 extension.reset(LoadAndExpectSuccess("devtools_extension.json")); 265 EXPECT_EQ(extension->url().spec() + "devtools.html", 266 extension->devtools_url().spec()); 267 *CommandLine::ForCurrentProcess() = old_command_line; 268} 269 270TEST_F(ExtensionManifestTest, DisallowHybridApps) { 271 LoadAndExpectError("disallow_hybrid_1.json", 272 errors::kHostedAppsCannotIncludeExtensionFeatures); 273 LoadAndExpectError("disallow_hybrid_2.json", 274 errors::kHostedAppsCannotIncludeExtensionFeatures); 275} 276 277TEST_F(ExtensionManifestTest, OptionsPageInApps) { 278 scoped_ptr<Extension> extension; 279 280 // Allow options page with absolute URL in hosed apps. 281 extension.reset( 282 LoadAndExpectSuccess("hosted_app_absolute_options.json")); 283 EXPECT_STREQ("http", 284 extension->options_url().scheme().c_str()); 285 EXPECT_STREQ("example.com", 286 extension->options_url().host().c_str()); 287 EXPECT_STREQ("options.html", 288 extension->options_url().ExtractFileName().c_str()); 289 290 // Forbid options page with relative URL in hosted apps. 291 LoadAndExpectError("hosted_app_relative_options.json", 292 errors::kInvalidOptionsPageInHostedApp); 293 294 // Forbid options page with non-(http|https) scheme in hosted app. 295 LoadAndExpectError("hosted_app_file_options.json", 296 errors::kInvalidOptionsPageInHostedApp); 297 298 // Forbid absolute URL for options page in packaged apps. 299 LoadAndExpectError("packaged_app_absolute_options.json", 300 errors::kInvalidOptionsPageExpectUrlInPackage); 301} 302 303TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) { 304 std::string error; 305 scoped_ptr<DictionaryValue> manifest( 306 LoadManifestFile("valid_app.json", &error)); 307 ASSERT_TRUE(manifest.get()); 308 309 ListValue *permissions = new ListValue(); 310 manifest->Set(keys::kPermissions, permissions); 311 for (size_t i = 0; i < Extension::kNumPermissions; i++) { 312 const char* name = Extension::kPermissions[i].name; 313 StringValue* p = new StringValue(name); 314 permissions->Clear(); 315 permissions->Append(p); 316 std::string message_name = StringPrintf("permission-%s", name); 317 if (Extension::IsHostedAppPermission(name)) { 318 scoped_ptr<Extension> extension; 319 extension.reset(LoadAndExpectSuccess(manifest.get(), message_name)); 320 } else { 321 LoadAndExpectError(manifest.get(), message_name, 322 errors::kInvalidPermission); 323 } 324 } 325} 326 327TEST_F(ExtensionManifestTest, NormalizeIconPaths) { 328 scoped_ptr<Extension> extension( 329 LoadAndExpectSuccess("normalize_icon_paths.json")); 330 EXPECT_EQ("16.png", 331 extension->icons().Get(16, ExtensionIconSet::MATCH_EXACTLY)); 332 EXPECT_EQ("48.png", 333 extension->icons().Get(48, ExtensionIconSet::MATCH_EXACTLY)); 334} 335 336TEST_F(ExtensionManifestTest, DisallowMultipleUISurfaces) { 337 LoadAndExpectError("multiple_ui_surfaces_1.json", errors::kOneUISurfaceOnly); 338 LoadAndExpectError("multiple_ui_surfaces_2.json", errors::kOneUISurfaceOnly); 339 LoadAndExpectError("multiple_ui_surfaces_3.json", errors::kOneUISurfaceOnly); 340} 341