| // Copyright (c) 2012 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/common/extensions/extension.h" |
| |
| #if defined(TOOLKIT_GTK) |
| #include <gtk/gtk.h> |
| #endif |
| |
| #include "base/command_line.h" |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #include "base/i18n/rtl.h" |
| #include "base/json/json_value_serializer.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/path_service.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/extensions/extension_action.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/extensions/extension_error_utils.h" |
| #include "chrome/common/extensions/file_browser_handler.h" |
| #include "chrome/common/extensions/url_pattern.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "webkit/glue/web_intent_service_data.h" |
| |
| namespace { |
| |
| static void AddPattern(URLPatternSet* extent, const std::string& pattern) { |
| int schemes = URLPattern::SCHEME_ALL; |
| extent->AddPattern(URLPattern(schemes, pattern)); |
| } |
| |
| } |
| |
| namespace errors = extension_manifest_errors; |
| namespace keys = extension_manifest_keys; |
| |
| class ExtensionManifestTest : public testing::Test { |
| public: |
| ExtensionManifestTest() : enable_apps_(true) {} |
| |
| protected: |
| static DictionaryValue* LoadManifestFile(const std::string& filename, |
| std::string* error) { |
| FilePath path; |
| PathService::Get(chrome::DIR_TEST_DATA, &path); |
| path = path.AppendASCII("extensions") |
| .AppendASCII("manifest_tests") |
| .AppendASCII(filename.c_str()); |
| EXPECT_TRUE(file_util::PathExists(path)) << |
| "Couldn't find " << path.value(); |
| |
| JSONFileValueSerializer serializer(path); |
| return static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error)); |
| } |
| |
| // Helper class that simplifies creating methods that take either a filename |
| // to a manifest or the manifest itself. |
| class Manifest { |
| public: |
| // Purposely not marked explicit for convenience. The vast majority of |
| // callers pass string literal. |
| Manifest(const char* name) |
| : name_(name), manifest_(NULL) { |
| } |
| Manifest(DictionaryValue* manifest, const char* name) |
| : name_(name), manifest_(manifest) { |
| } |
| Manifest(const Manifest& m) { |
| // C++98 requires the copy constructor for a type to be visiable if you |
| // take a const-ref of a temporary for that type. Since Manifest |
| // contains a scoped_ptr, its implicit copy constructor is declared |
| // Manifest(Manifest&) according to spec 12.8.5. This breaks the first |
| // requirement and thus you cannot use it with LoadAndExpectError() or |
| // LoadAndExpectSuccess() easily. |
| // |
| // To get around this spec pedantry, we declare the copy constructor |
| // explicitly. It will never get invoked. |
| NOTREACHED(); |
| } |
| |
| |
| const std::string& name() const { return name_; } |
| |
| DictionaryValue* GetManifest(std::string* error) const { |
| if (manifest_) |
| return manifest_; |
| |
| manifest_ = LoadManifestFile(name_, error); |
| manifest_holder_.reset(manifest_); |
| return manifest_; |
| } |
| |
| private: |
| std::string name_; |
| mutable DictionaryValue* manifest_; |
| mutable scoped_ptr<DictionaryValue> manifest_holder_; |
| }; |
| |
| scoped_refptr<Extension> LoadExtension( |
| const Manifest& manifest, |
| std::string* error, |
| Extension::Location location = Extension::INTERNAL, |
| int flags = Extension::NO_FLAGS) { |
| DictionaryValue* value = manifest.GetManifest(error); |
| if (!value) |
| return NULL; |
| FilePath path; |
| PathService::Get(chrome::DIR_TEST_DATA, &path); |
| path = path.AppendASCII("extensions").AppendASCII("manifest_tests"); |
| return Extension::Create(path.DirName(), location, *value, flags, error); |
| } |
| |
| scoped_refptr<Extension> LoadAndExpectSuccess( |
| const Manifest& manifest, |
| Extension::Location location = Extension::INTERNAL, |
| int flags = Extension::NO_FLAGS) { |
| std::string error; |
| scoped_refptr<Extension> extension = |
| LoadExtension(manifest, &error, location, flags); |
| EXPECT_TRUE(extension) << manifest.name(); |
| EXPECT_EQ("", error) << manifest.name(); |
| return extension; |
| } |
| |
| void VerifyExpectedError(Extension* extension, |
| const std::string& name, |
| const std::string& error, |
| const std::string& expected_error) { |
| EXPECT_FALSE(extension) << |
| "Expected failure loading extension '" << name << |
| "', but didn't get one."; |
| EXPECT_TRUE(MatchPattern(error, expected_error)) << name << |
| " expected '" << expected_error << "' but got '" << error << "'"; |
| } |
| |
| void LoadAndExpectError(const Manifest& manifest, |
| const std::string& expected_error, |
| Extension::Location location = Extension::INTERNAL, |
| int flags = Extension::NO_FLAGS) { |
| std::string error; |
| scoped_refptr<Extension> extension( |
| LoadExtension(manifest, &error, location, flags)); |
| VerifyExpectedError(extension.get(), manifest.name(), error, |
| expected_error); |
| } |
| |
| struct Testcase { |
| std::string manifest; |
| std::string expected_error; |
| }; |
| |
| void RunTestcases(const Testcase* testcases, size_t num_testcases) { |
| for (size_t i = 0; i < num_testcases; ++i) { |
| LoadAndExpectError(testcases[i].manifest.c_str(), |
| testcases[i].expected_error); |
| } |
| } |
| |
| bool enable_apps_; |
| }; |
| |
| TEST_F(ExtensionManifestTest, InitFromValueInvalid) { |
| Testcase testcases[] = { |
| {"init_invalid_version_missing.json", errors::kInvalidVersion}, |
| {"init_invalid_version_invalid.json", errors::kInvalidVersion}, |
| {"init_invalid_name_missing.json", errors::kInvalidName}, |
| {"init_invalid_name_invalid.json", errors::kInvalidName}, |
| {"init_invalid_description_invalid.json", errors::kInvalidDescription}, |
| {"init_invalid_icons_invalid.json", errors::kInvalidIcons}, |
| {"init_invalid_icons_path_invalid.json", errors::kInvalidIconPath}, |
| {"init_invalid_script_invalid.json", errors::kInvalidContentScriptsList}, |
| {"init_invalid_script_item_invalid.json", errors::kInvalidContentScript}, |
| {"init_invalid_script_matches_missing.json", errors::kInvalidMatches}, |
| {"init_invalid_script_matches_invalid.json", errors::kInvalidMatches}, |
| {"init_invalid_script_matches_empty.json", errors::kInvalidMatchCount}, |
| {"init_invalid_script_match_item_invalid.json", errors::kInvalidMatch}, |
| {"init_invalid_script_match_item_invalid_2.json", errors::kInvalidMatch}, |
| {"init_invalid_script_files_missing.json", errors::kMissingFile}, |
| {"init_invalid_files_js_invalid.json", errors::kInvalidJsList}, |
| {"init_invalid_files_empty.json", errors::kMissingFile}, |
| {"init_invalid_files_js_empty_css_missing.json", errors::kMissingFile}, |
| {"init_invalid_files_js_item_invalid.json", errors::kInvalidJs}, |
| {"init_invalid_files_css_invalid.json", errors::kInvalidCssList}, |
| {"init_invalid_files_css_item_invalid.json", errors::kInvalidCss}, |
| {"init_invalid_permissions_invalid.json", errors::kInvalidPermissions}, |
| {"init_invalid_permissions_item_invalid.json", errors::kInvalidPermission}, |
| {"init_invalid_page_actions_multi.json", |
| errors::kInvalidPageActionsListSize}, |
| {"init_invalid_options_url_invalid.json", errors::kInvalidOptionsPage}, |
| {"init_invalid_locale_invalid.json", errors::kInvalidDefaultLocale}, |
| {"init_invalid_locale_empty.json", errors::kInvalidDefaultLocale}, |
| {"init_invalid_min_chrome_invalid.json", |
| errors::kInvalidMinimumChromeVersion}, |
| {"init_invalid_chrome_version_too_low.json", errors::kChromeVersionTooLow}, |
| {"init_invalid_requirements_1.json", errors::kInvalidRequirements}, |
| {"init_invalid_requirements_2.json", errors::kInvalidRequirement} |
| }; |
| |
| RunTestcases(testcases, arraysize(testcases)); |
| } |
| |
| TEST_F(ExtensionManifestTest, InitFromValueValid) { |
| scoped_refptr<Extension> extension(LoadAndExpectSuccess( |
| "init_valid_minimal.json")); |
| |
| FilePath path; |
| PathService::Get(chrome::DIR_TEST_DATA, &path); |
| path = path.AppendASCII("extensions"); |
| |
| EXPECT_TRUE(Extension::IdIsValid(extension->id())); |
| EXPECT_EQ("1.0.0.0", extension->VersionString()); |
| EXPECT_EQ("my extension", extension->name()); |
| EXPECT_EQ(extension->id(), extension->url().host()); |
| EXPECT_EQ(extension->path(), path); |
| EXPECT_EQ(path, extension->path()); |
| |
| // Test permissions scheme. |
| // We allow unknown API permissions, so this will be valid until we better |
| // distinguish between API and host permissions. |
| extension = LoadAndExpectSuccess("init_valid_permissions.json"); |
| |
| // Test with an options page. |
| extension = LoadAndExpectSuccess("init_valid_options.json"); |
| EXPECT_EQ("chrome-extension", extension->options_url().scheme()); |
| EXPECT_EQ("/options.html", extension->options_url().path()); |
| |
| // Test that an empty list of page actions does not stop a browser action |
| // from being loaded. |
| LoadAndExpectSuccess("init_valid_empty_page_actions.json"); |
| |
| // Test with a minimum_chrome_version. |
| LoadAndExpectSuccess("init_valid_minimum_chrome.json"); |
| |
| // Test a hosted app with a minimum_chrome_version. |
| LoadAndExpectSuccess("init_valid_app_minimum_chrome.json"); |
| |
| // Test a hosted app with a requirements section. |
| LoadAndExpectSuccess("init_valid_app_requirements.json"); |
| |
| // Verify empty permission settings are considered valid. |
| LoadAndExpectSuccess("init_valid_permissions_empty.json"); |
| |
| // We allow unknown API permissions, so this will be valid until we better |
| // distinguish between API and host permissions. |
| LoadAndExpectSuccess("init_valid_permissions_unknown.json"); |
| } |
| |
| TEST_F(ExtensionManifestTest, PlatformApps) { |
| CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnablePlatformApps); |
| |
| // A minimal platform app. |
| LoadAndExpectSuccess("init_valid_platform_app.json"); |
| } |
| |
| TEST_F(ExtensionManifestTest, InitFromValueValidNameInRTL) { |
| #if defined(TOOLKIT_GTK) |
| GtkTextDirection gtk_dir = gtk_widget_get_default_direction(); |
| gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); |
| #else |
| std::string locale = l10n_util::GetApplicationLocale(""); |
| base::i18n::SetICUDefaultLocale("he"); |
| #endif |
| |
| // No strong RTL characters in name. |
| scoped_refptr<Extension> extension(LoadAndExpectSuccess( |
| "init_valid_name_no_rtl.json")); |
| |
| string16 localized_name(ASCIIToUTF16("Dictionary (by Google)")); |
| base::i18n::AdjustStringForLocaleDirection(&localized_name); |
| EXPECT_EQ(localized_name, UTF8ToUTF16(extension->name())); |
| |
| // Strong RTL characters in name. |
| extension = LoadAndExpectSuccess("init_valid_name_strong_rtl.json"); |
| |
| localized_name = WideToUTF16(L"Dictionary (\x05D1\x05D2"L" Google)"); |
| base::i18n::AdjustStringForLocaleDirection(&localized_name); |
| EXPECT_EQ(localized_name, UTF8ToUTF16(extension->name())); |
| |
| // Reset locale. |
| #if defined(TOOLKIT_GTK) |
| gtk_widget_set_default_direction(gtk_dir); |
| #else |
| base::i18n::SetICUDefaultLocale(locale); |
| #endif |
| } |
| |
| TEST_F(ExtensionManifestTest, UpdateUrls) { |
| // Test several valid update urls |
| LoadAndExpectSuccess("update_url_valid_1.json", Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| LoadAndExpectSuccess("update_url_valid_2.json", Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| LoadAndExpectSuccess("update_url_valid_3.json", Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| LoadAndExpectSuccess("update_url_valid_4.json", Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| |
| // Test some invalid update urls |
| LoadAndExpectError("update_url_invalid_1.json", errors::kInvalidUpdateURL, |
| Extension::INTERNAL, Extension::STRICT_ERROR_CHECKS); |
| LoadAndExpectError("update_url_invalid_2.json", errors::kInvalidUpdateURL, |
| Extension::INTERNAL, Extension::STRICT_ERROR_CHECKS); |
| LoadAndExpectError("update_url_invalid_3.json", errors::kInvalidUpdateURL, |
| Extension::INTERNAL, Extension::STRICT_ERROR_CHECKS); |
| } |
| |
| // Tests that the old permission name "unlimited_storage" still works for |
| // backwards compatibility (we renamed it to "unlimitedStorage"). |
| TEST_F(ExtensionManifestTest, OldUnlimitedStoragePermission) { |
| scoped_refptr<Extension> extension = LoadAndExpectSuccess( |
| "old_unlimited_storage.json", Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| EXPECT_TRUE(extension->HasAPIPermission( |
| ExtensionAPIPermission::kUnlimitedStorage)); |
| } |
| |
| TEST_F(ExtensionManifestTest, ValidApp) { |
| scoped_refptr<Extension> extension(LoadAndExpectSuccess("valid_app.json")); |
| URLPatternSet expected_patterns; |
| AddPattern(&expected_patterns, "http://www.google.com/mail/*"); |
| AddPattern(&expected_patterns, "http://www.google.com/foobar/*"); |
| EXPECT_EQ(expected_patterns, extension->web_extent()); |
| EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); |
| EXPECT_EQ("http://www.google.com/mail/", extension->launch_web_url()); |
| } |
| |
| TEST_F(ExtensionManifestTest, AppWebUrls) { |
| LoadAndExpectError("web_urls_wrong_type.json", |
| errors::kInvalidWebURLs); |
| LoadAndExpectError( |
| "web_urls_invalid_1.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidWebURL, |
| base::IntToString(0), |
| errors::kExpectString)); |
| |
| LoadAndExpectError( |
| "web_urls_invalid_2.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidWebURL, |
| base::IntToString(0), |
| URLPattern::GetParseResultString( |
| URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR))); |
| |
| LoadAndExpectError( |
| "web_urls_invalid_3.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidWebURL, |
| base::IntToString(0), |
| errors::kNoWildCardsInPaths)); |
| |
| LoadAndExpectError( |
| "web_urls_invalid_4.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidWebURL, |
| base::IntToString(0), |
| errors::kCannotClaimAllURLsInExtent)); |
| |
| LoadAndExpectError( |
| "web_urls_invalid_5.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidWebURL, |
| base::IntToString(1), |
| errors::kCannotClaimAllHostsInExtent)); |
| |
| LoadAndExpectSuccess("web_urls_has_port.json"); |
| |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("web_urls_default.json")); |
| ASSERT_EQ(1u, extension->web_extent().patterns().size()); |
| EXPECT_EQ("*://www.google.com/*", |
| extension->web_extent().patterns().begin()->GetAsString()); |
| } |
| |
| TEST_F(ExtensionManifestTest, AppLaunchContainer) { |
| scoped_refptr<Extension> extension; |
| |
| extension = LoadAndExpectSuccess("launch_tab.json"); |
| EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); |
| |
| extension = LoadAndExpectSuccess("launch_panel.json"); |
| EXPECT_EQ(extension_misc::LAUNCH_PANEL, extension->launch_container()); |
| |
| extension = LoadAndExpectSuccess("launch_default.json"); |
| EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); |
| |
| extension = LoadAndExpectSuccess("launch_width.json"); |
| EXPECT_EQ(640, extension->launch_width()); |
| |
| extension = LoadAndExpectSuccess("launch_height.json"); |
| EXPECT_EQ(480, extension->launch_height()); |
| |
| LoadAndExpectError("launch_window.json", |
| errors::kInvalidLaunchContainer); |
| LoadAndExpectError("launch_container_invalid_type.json", |
| errors::kInvalidLaunchContainer); |
| LoadAndExpectError("launch_container_invalid_value.json", |
| errors::kInvalidLaunchContainer); |
| LoadAndExpectError("launch_container_without_launch_url.json", |
| errors::kLaunchURLRequired); |
| LoadAndExpectError("launch_width_invalid.json", |
| errors::kInvalidLaunchWidthContainer); |
| LoadAndExpectError("launch_width_negative.json", |
| errors::kInvalidLaunchWidth); |
| LoadAndExpectError("launch_height_invalid.json", |
| errors::kInvalidLaunchHeightContainer); |
| LoadAndExpectError("launch_height_negative.json", |
| errors::kInvalidLaunchHeight); |
| } |
| |
| TEST_F(ExtensionManifestTest, PlatformAppLaunchContainer) { |
| CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnablePlatformApps); |
| |
| LoadAndExpectError("launch_container_invalid_type_for_platform.json", |
| errors::kInvalidLaunchContainerForPlatform); |
| } |
| |
| TEST_F(ExtensionManifestTest, AppLaunchURL) { |
| LoadAndExpectError("launch_path_and_url.json", |
| errors::kLaunchPathAndURLAreExclusive); |
| LoadAndExpectError("launch_path_and_extent.json", |
| errors::kLaunchPathAndExtentAreExclusive); |
| LoadAndExpectError("launch_path_invalid_type.json", |
| errors::kInvalidLaunchLocalPath); |
| LoadAndExpectError("launch_path_invalid_value.json", |
| errors::kInvalidLaunchLocalPath); |
| LoadAndExpectError("launch_url_invalid_type_1.json", |
| errors::kInvalidLaunchWebURL); |
| LoadAndExpectError("launch_url_invalid_type_2.json", |
| errors::kInvalidLaunchWebURL); |
| LoadAndExpectError("launch_url_invalid_type_3.json", |
| errors::kInvalidLaunchWebURL); |
| |
| scoped_refptr<Extension> extension; |
| extension = LoadAndExpectSuccess("launch_local_path.json"); |
| EXPECT_EQ(extension->url().spec() + "launch.html", |
| extension->GetFullLaunchURL().spec()); |
| |
| LoadAndExpectError("launch_web_url_relative.json", |
| errors::kInvalidLaunchWebURL); |
| |
| extension = LoadAndExpectSuccess("launch_web_url_absolute.json"); |
| EXPECT_EQ(GURL("http://www.google.com/launch.html"), |
| extension->GetFullLaunchURL()); |
| } |
| |
| TEST_F(ExtensionManifestTest, Override) { |
| LoadAndExpectError("override_newtab_and_history.json", |
| errors::kMultipleOverrides); |
| LoadAndExpectError("override_invalid_page.json", |
| errors::kInvalidChromeURLOverrides); |
| |
| scoped_refptr<Extension> extension; |
| |
| extension = LoadAndExpectSuccess("override_new_tab.json"); |
| EXPECT_EQ(extension->url().spec() + "newtab.html", |
| extension->GetChromeURLOverrides().find("newtab")->second.spec()); |
| |
| extension = LoadAndExpectSuccess("override_history.json"); |
| EXPECT_EQ(extension->url().spec() + "history.html", |
| extension->GetChromeURLOverrides().find("history")->second.spec()); |
| } |
| |
| TEST_F(ExtensionManifestTest, ChromeURLPermissionInvalid) { |
| LoadAndExpectError("permission_chrome_url_invalid.json", |
| errors::kInvalidPermissionScheme); |
| } |
| |
| TEST_F(ExtensionManifestTest, ChromeResourcesPermissionValidOnlyForComponents) { |
| LoadAndExpectError("permission_chrome_resources_url.json", |
| errors::kInvalidPermissionScheme); |
| std::string error; |
| scoped_refptr<Extension> extension; |
| extension = LoadExtension( |
| "permission_chrome_resources_url.json", |
| &error, |
| Extension::COMPONENT, |
| Extension::STRICT_ERROR_CHECKS); |
| EXPECT_EQ("", error); |
| } |
| |
| TEST_F(ExtensionManifestTest, ContentScriptMatchPattern) { |
| // chrome:// urls are not allowed. |
| LoadAndExpectError( |
| "content_script_chrome_url_invalid.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidMatch, |
| base::IntToString(0), |
| base::IntToString(0), |
| URLPattern::GetParseResultString( |
| URLPattern::PARSE_ERROR_INVALID_SCHEME))); |
| |
| // Match paterns must be strings. |
| LoadAndExpectError( |
| "content_script_match_pattern_not_string.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidMatch, |
| base::IntToString(0), |
| base::IntToString(0), |
| errors::kExpectString)); |
| |
| LoadAndExpectSuccess("ports_in_content_scripts.json"); |
| } |
| |
| TEST_F(ExtensionManifestTest, ExcludeMatchPatterns) { |
| LoadAndExpectSuccess("exclude_matches.json"); |
| LoadAndExpectSuccess("exclude_matches_empty.json"); |
| |
| LoadAndExpectError("exclude_matches_not_list.json", |
| "Invalid value for 'content_scripts[0].exclude_matches'."); |
| |
| LoadAndExpectError("exclude_matches_invalid_host.json", |
| "Invalid value for " |
| "'content_scripts[0].exclude_matches[0]': " |
| "Invalid host wildcard."); |
| } |
| |
| TEST_F(ExtensionManifestTest, ExperimentalPermission) { |
| LoadAndExpectError("experimental.json", errors::kExperimentalFlagRequired); |
| LoadAndExpectSuccess("experimental.json", Extension::COMPONENT); |
| LoadAndExpectSuccess("experimental.json", Extension::INTERNAL, |
| Extension::FROM_WEBSTORE); |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| LoadAndExpectSuccess("experimental.json"); |
| } |
| |
| TEST_F(ExtensionManifestTest, DevToolsExtensions) { |
| LoadAndExpectError("devtools_extension_url_invalid_type.json", |
| errors::kInvalidDevToolsPage); |
| |
| scoped_refptr<Extension> extension; |
| extension = LoadAndExpectSuccess("devtools_extension.json"); |
| EXPECT_EQ(extension->url().spec() + "devtools.html", |
| extension->devtools_url().spec()); |
| EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); |
| } |
| |
| TEST_F(ExtensionManifestTest, BackgroundPermission) { |
| LoadAndExpectError("background_permission.json", |
| errors::kBackgroundPermissionNeeded); |
| } |
| |
| TEST_F(ExtensionManifestTest, OptionsPageInApps) { |
| scoped_refptr<Extension> extension; |
| |
| // Allow options page with absolute URL in hosted apps. |
| extension = LoadAndExpectSuccess("hosted_app_absolute_options.json"); |
| EXPECT_STREQ("http", |
| extension->options_url().scheme().c_str()); |
| EXPECT_STREQ("example.com", |
| extension->options_url().host().c_str()); |
| EXPECT_STREQ("options.html", |
| extension->options_url().ExtractFileName().c_str()); |
| |
| // Forbid options page with relative URL in hosted apps. |
| LoadAndExpectError("hosted_app_relative_options.json", |
| errors::kInvalidOptionsPageInHostedApp); |
| |
| // Forbid options page with non-(http|https) scheme in hosted app. |
| LoadAndExpectError("hosted_app_file_options.json", |
| errors::kInvalidOptionsPageInHostedApp); |
| |
| // Forbid absolute URL for options page in packaged apps. |
| LoadAndExpectError("packaged_app_absolute_options.json", |
| errors::kInvalidOptionsPageExpectUrlInPackage); |
| } |
| |
| TEST_F(ExtensionManifestTest, HostedAppPermissions) { |
| std::string error; |
| scoped_ptr<DictionaryValue> manifest( |
| LoadManifestFile("hosted_app_absolute_options.json", &error)); |
| ASSERT_TRUE(manifest.get()); |
| ListValue* permissions = NULL; |
| ASSERT_TRUE(manifest->GetList("permissions", &permissions)); |
| |
| int platform_app = ExtensionAPIPermission::kTypePlatformApp; |
| ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); |
| ExtensionAPIPermissionSet api_perms = info->GetAll(); |
| for (ExtensionAPIPermissionSet::iterator i = api_perms.begin(); |
| i != api_perms.end(); ++i) { |
| if (*i == ExtensionAPIPermission::kExperimental) |
| continue; |
| |
| ExtensionAPIPermission* permission = info->GetByID(*i); |
| const char* name = permission->name(); |
| StringValue* p = new StringValue(name); |
| permissions->Clear(); |
| permissions->Append(p); |
| |
| // Some permissions are only available to component hosted apps. |
| if (permission->is_component_only()) { |
| LoadAndExpectError(Manifest(manifest.get(), name), |
| errors::kPermissionNotAllowed, |
| Extension::INTERNAL); |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess(Manifest(manifest.get(), name), |
| Extension::COMPONENT)); |
| EXPECT_TRUE(extension->GetActivePermissions()->HasAPIPermission( |
| permission->id())); |
| |
| } else if (permission->type_restrictions() == platform_app) { |
| LoadAndExpectError(Manifest(manifest.get(), name), |
| errors::kPermissionNotAllowed, |
| Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| } else if (!permission->supports_hosted_apps()) { |
| // Most normal extension permissions also aren't available to hosted apps. |
| // For these, the error is only reported in strict mode for legacy |
| // reasons: crbug.com/101993. |
| LoadAndExpectError(Manifest(manifest.get(), name), |
| errors::kPermissionNotAllowed, |
| Extension::INTERNAL, |
| Extension::STRICT_ERROR_CHECKS); |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess(Manifest(manifest.get(), name), |
| Extension::INTERNAL)); |
| EXPECT_FALSE(extension->GetActivePermissions()->HasAPIPermission( |
| permission->id())); |
| |
| // These permissions are also allowed for component hosted apps. |
| extension = LoadAndExpectSuccess(Manifest(manifest.get(), name), |
| Extension::COMPONENT); |
| EXPECT_TRUE(extension->GetActivePermissions()->HasAPIPermission( |
| permission->id())); |
| |
| } else { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess(Manifest(manifest.get(), name))); |
| EXPECT_TRUE(extension->GetActivePermissions()->HasAPIPermission( |
| permission->id())); |
| } |
| } |
| } |
| |
| TEST_F(ExtensionManifestTest, ComponentOnlyPermission) { |
| std::string error; |
| scoped_ptr<DictionaryValue> manifest( |
| LoadManifestFile("init_valid_minimal.json", &error)); |
| ASSERT_TRUE(manifest.get()); |
| ListValue* permissions = new ListValue(); |
| manifest->Set("permissions", permissions); |
| |
| ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); |
| ExtensionAPIPermissionSet api_perms = info->GetAll(); |
| for (ExtensionAPIPermissionSet::iterator i = api_perms.begin(); |
| i != api_perms.end(); ++i) { |
| if (*i == ExtensionAPIPermission::kExperimental) |
| continue; |
| |
| ExtensionAPIPermission* permission = info->GetByID(*i); |
| const char* name = permission->name(); |
| StringValue* p = new StringValue(name); |
| permissions->Clear(); |
| permissions->Append(p); |
| |
| if (!permission->is_component_only()) |
| continue; |
| |
| // Component-only extensions should only be enabled for component |
| // extensions. |
| LoadAndExpectError(Manifest(manifest.get(), name), |
| errors::kPermissionNotAllowed); |
| LoadAndExpectSuccess(Manifest(manifest.get(), name), |
| Extension::COMPONENT); |
| } |
| } |
| |
| TEST_F(ExtensionManifestTest, AllowUnrecognizedPermissions) { |
| std::string error; |
| scoped_ptr<DictionaryValue> manifest( |
| LoadManifestFile("valid_app.json", &error)); |
| ListValue* permissions = NULL; |
| ASSERT_TRUE(manifest->GetList("permissions", &permissions)); |
| permissions->Append(new StringValue("not-a-valid-permission")); |
| LoadAndExpectSuccess(Manifest(manifest.get(), "")); |
| } |
| |
| TEST_F(ExtensionManifestTest, NormalizeIconPaths) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("normalize_icon_paths.json")); |
| EXPECT_EQ("16.png", |
| extension->icons().Get(16, ExtensionIconSet::MATCH_EXACTLY)); |
| EXPECT_EQ("48.png", |
| extension->icons().Get(48, ExtensionIconSet::MATCH_EXACTLY)); |
| } |
| |
| TEST_F(ExtensionManifestTest, DisallowMultipleUISurfaces) { |
| LoadAndExpectError("multiple_ui_surfaces.json", errors::kOneUISurfaceOnly); |
| } |
| |
| TEST_F(ExtensionManifestTest, ParseHomepageURLs) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("homepage_valid.json")); |
| LoadAndExpectError("homepage_empty.json", |
| extension_manifest_errors::kInvalidHomepageURL); |
| LoadAndExpectError("homepage_invalid.json", |
| extension_manifest_errors::kInvalidHomepageURL); |
| LoadAndExpectError("homepage_bad_schema.json", |
| extension_manifest_errors::kInvalidHomepageURL); |
| } |
| |
| TEST_F(ExtensionManifestTest, GetHomepageURL) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("homepage_valid.json")); |
| EXPECT_EQ(GURL("http://foo.com#bar"), extension->GetHomepageURL()); |
| |
| // The Google Gallery URL ends with the id, which depends on the path, which |
| // can be different in testing, so we just check the part before id. |
| extension = LoadAndExpectSuccess("homepage_google_hosted.json"); |
| EXPECT_TRUE(StartsWithASCII(extension->GetHomepageURL().spec(), |
| "https://chrome.google.com/webstore/detail/", |
| false)); |
| |
| extension = LoadAndExpectSuccess("homepage_externally_hosted.json"); |
| EXPECT_EQ(GURL(), extension->GetHomepageURL()); |
| } |
| |
| TEST_F(ExtensionManifestTest, DefaultPathForExtent) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("default_path_for_extent.json")); |
| |
| ASSERT_EQ(1u, extension->web_extent().patterns().size()); |
| EXPECT_EQ("/*", extension->web_extent().patterns().begin()->path()); |
| EXPECT_TRUE(extension->web_extent().MatchesURL( |
| GURL("http://www.google.com/monkey"))); |
| } |
| |
| TEST_F(ExtensionManifestTest, DefaultLocale) { |
| LoadAndExpectError("default_locale_invalid.json", |
| extension_manifest_errors::kInvalidDefaultLocale); |
| |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("default_locale_valid.json")); |
| EXPECT_EQ("de-AT", extension->default_locale()); |
| } |
| |
| TEST_F(ExtensionManifestTest, TtsEngine) { |
| LoadAndExpectError("tts_engine_invalid_1.json", |
| extension_manifest_errors::kInvalidTts); |
| LoadAndExpectError("tts_engine_invalid_2.json", |
| extension_manifest_errors::kInvalidTtsVoices); |
| LoadAndExpectError("tts_engine_invalid_3.json", |
| extension_manifest_errors::kInvalidTtsVoices); |
| LoadAndExpectError("tts_engine_invalid_4.json", |
| extension_manifest_errors::kInvalidTtsVoicesVoiceName); |
| LoadAndExpectError("tts_engine_invalid_5.json", |
| extension_manifest_errors::kInvalidTtsVoicesLang); |
| LoadAndExpectError("tts_engine_invalid_6.json", |
| extension_manifest_errors::kInvalidTtsVoicesLang); |
| LoadAndExpectError("tts_engine_invalid_7.json", |
| extension_manifest_errors::kInvalidTtsVoicesGender); |
| LoadAndExpectError("tts_engine_invalid_8.json", |
| extension_manifest_errors::kInvalidTtsVoicesEventTypes); |
| LoadAndExpectError("tts_engine_invalid_9.json", |
| extension_manifest_errors::kInvalidTtsVoicesEventTypes); |
| |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("tts_engine_valid.json")); |
| |
| ASSERT_EQ(1u, extension->tts_voices().size()); |
| EXPECT_EQ("name", extension->tts_voices()[0].voice_name); |
| EXPECT_EQ("en-US", extension->tts_voices()[0].lang); |
| EXPECT_EQ("female", extension->tts_voices()[0].gender); |
| EXPECT_EQ(3U, extension->tts_voices()[0].event_types.size()); |
| } |
| |
| TEST_F(ExtensionManifestTest, WebIntents) { |
| LoadAndExpectError("intent_invalid_1.json", |
| extension_manifest_errors::kInvalidIntents); |
| LoadAndExpectError("intent_invalid_2.json", |
| extension_manifest_errors::kInvalidIntent); |
| LoadAndExpectError("intent_invalid_3.json", |
| extension_manifest_errors::kInvalidIntentPath); |
| LoadAndExpectError("intent_invalid_4.json", |
| extension_manifest_errors::kInvalidIntentDisposition); |
| LoadAndExpectError("intent_invalid_5.json", |
| extension_manifest_errors::kInvalidIntentType); |
| LoadAndExpectError("intent_invalid_6.json", |
| extension_manifest_errors::kInvalidIntentTitle); |
| |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("intent_valid.json")); |
| ASSERT_TRUE(extension.get() != NULL); |
| |
| ASSERT_EQ(1u, extension->intents_services().size()); |
| EXPECT_EQ("image/png", UTF16ToUTF8(extension->intents_services()[0].type)); |
| EXPECT_EQ("http://webintents.org/share", |
| UTF16ToUTF8(extension->intents_services()[0].action)); |
| EXPECT_EQ("chrome-extension", |
| extension->intents_services()[0].service_url.scheme()); |
| EXPECT_EQ("///services/share", |
| extension->intents_services()[0].service_url.path()); |
| EXPECT_EQ("Sample Sharing Intent", |
| UTF16ToUTF8(extension->intents_services()[0].title)); |
| EXPECT_EQ(webkit_glue::WebIntentServiceData::DISPOSITION_INLINE, |
| extension->intents_services()[0].disposition); |
| |
| // Verify that optional fields are filled with defaults. |
| extension = LoadAndExpectSuccess("intent_valid_minimal.json"); |
| ASSERT_TRUE(extension.get() != NULL); |
| |
| ASSERT_EQ(1u, extension->intents_services().size()); |
| EXPECT_EQ("*", UTF16ToUTF8(extension->intents_services()[0].type)); |
| EXPECT_EQ("http://webintents.org/share", |
| UTF16ToUTF8(extension->intents_services()[0].action)); |
| EXPECT_TRUE(extension->intents_services()[0].service_url.is_empty()); |
| EXPECT_EQ("", UTF16ToUTF8(extension->intents_services()[0].title)); |
| EXPECT_EQ(webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW, |
| extension->intents_services()[0].disposition); |
| } |
| |
| TEST_F(ExtensionManifestTest, WebIntentsWithMultipleMimeTypes) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("intent_valid_multitype.json")); |
| ASSERT_TRUE(extension.get() != NULL); |
| |
| ASSERT_EQ(2u, extension->intents_services().size()); |
| |
| // One registration with multiple types generates a separate service for |
| // each MIME type. |
| for (int i = 0; i < 2; ++i) { |
| EXPECT_EQ("http://webintents.org/share", |
| UTF16ToUTF8(extension->intents_services()[i].action)); |
| EXPECT_EQ("chrome-extension", |
| extension->intents_services()[i].service_url.scheme()); |
| EXPECT_EQ("///services/share", |
| extension->intents_services()[i].service_url.path()); |
| EXPECT_EQ("Sample Sharing Intent", |
| UTF16ToUTF8(extension->intents_services()[i].title)); |
| EXPECT_EQ(webkit_glue::WebIntentServiceData::DISPOSITION_INLINE, |
| extension->intents_services()[i].disposition); |
| } |
| EXPECT_EQ("image/jpeg", UTF16ToUTF8(extension->intents_services()[0].type)); |
| EXPECT_EQ("image/bmp", UTF16ToUTF8(extension->intents_services()[1].type)); |
| |
| LoadAndExpectError("intent_invalid_type_element.json", |
| extension_manifest_errors::kInvalidIntentTypeElement); |
| } |
| |
| TEST_F(ExtensionManifestTest, PortsInPermissions) { |
| // Loading as a user would shoud not trigger an error. |
| LoadAndExpectSuccess("ports_in_permissions.json"); |
| } |
| |
| TEST_F(ExtensionManifestTest, WebAccessibleResources) { |
| // Manifest version 2 with web accessible resources specified. |
| scoped_refptr<Extension> extension1( |
| LoadAndExpectSuccess("web_accessible_resources_1.json")); |
| |
| // Manifest version 2 with no web accessible resources. |
| scoped_refptr<Extension> extension2( |
| LoadAndExpectSuccess("web_accessible_resources_2.json")); |
| |
| // Default manifest version with web accessible resources specified. |
| scoped_refptr<Extension> extension3( |
| LoadAndExpectSuccess("web_accessible_resources_3.json")); |
| |
| // Default manifest version with no web accessible resources. |
| scoped_refptr<Extension> extension4( |
| LoadAndExpectSuccess("web_accessible_resources_4.json")); |
| |
| EXPECT_TRUE(extension1->HasWebAccessibleResources()); |
| EXPECT_FALSE(extension2->HasWebAccessibleResources()); |
| EXPECT_TRUE(extension3->HasWebAccessibleResources()); |
| EXPECT_FALSE(extension4->HasWebAccessibleResources()); |
| |
| EXPECT_TRUE(extension1->IsResourceWebAccessible("/test")); |
| EXPECT_FALSE(extension1->IsResourceWebAccessible("/none")); |
| |
| EXPECT_FALSE(extension2->IsResourceWebAccessible("/test")); |
| |
| EXPECT_TRUE(extension3->IsResourceWebAccessible("/test")); |
| EXPECT_FALSE(extension3->IsResourceWebAccessible("/none")); |
| |
| EXPECT_TRUE(extension4->IsResourceWebAccessible("/test")); |
| EXPECT_TRUE(extension4->IsResourceWebAccessible("/none")); |
| } |
| |
| TEST_F(ExtensionManifestTest, IsolatedApps) { |
| // Requires --enable-experimental-extension-apis |
| LoadAndExpectError("isolated_app_valid.json", |
| errors::kExperimentalFlagRequired); |
| |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableExperimentalExtensionApis); |
| scoped_refptr<Extension> extension2( |
| LoadAndExpectSuccess("isolated_app_valid.json")); |
| EXPECT_TRUE(extension2->is_storage_isolated()); |
| } |
| |
| |
| TEST_F(ExtensionManifestTest, FileBrowserHandlers) { |
| LoadAndExpectError("filebrowser_invalid_actions_1.json", |
| errors::kInvalidFileBrowserHandler); |
| LoadAndExpectError("filebrowser_invalid_actions_2.json", |
| errors::kInvalidFileBrowserHandler); |
| LoadAndExpectError("filebrowser_invalid_action_id.json", |
| errors::kInvalidPageActionId); |
| LoadAndExpectError("filebrowser_invalid_action_title.json", |
| errors::kInvalidPageActionDefaultTitle); |
| LoadAndExpectError("filebrowser_invalid_action_id.json", |
| errors::kInvalidPageActionId); |
| LoadAndExpectError("filebrowser_invalid_file_filters_1.json", |
| errors::kInvalidFileFiltersList); |
| LoadAndExpectError("filebrowser_invalid_file_filters_2.json", |
| ExtensionErrorUtils::FormatErrorMessage( |
| errors::kInvalidFileFilterValue, base::IntToString(0))); |
| LoadAndExpectError("filebrowser_invalid_file_filters_url.json", |
| ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidURLPatternError, |
| "http:*.html")); |
| |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("filebrowser_valid.json")); |
| ASSERT_TRUE(extension->file_browser_handlers() != NULL); |
| ASSERT_EQ(extension->file_browser_handlers()->size(), 1U); |
| const FileBrowserHandler* action = |
| extension->file_browser_handlers()->at(0).get(); |
| EXPECT_EQ(action->title(), "Default title"); |
| EXPECT_EQ(action->icon_path(), "icon.png"); |
| const URLPatternSet& patterns = action->file_url_patterns(); |
| ASSERT_EQ(patterns.patterns().size(), 1U); |
| ASSERT_TRUE(action->MatchesURL( |
| GURL("filesystem:chrome-extension://foo/local/test.txt"))); |
| } |
| |
| TEST_F(ExtensionManifestTest, FileManagerURLOverride) { |
| // A component extention can override chrome://files/ URL. |
| std::string error; |
| scoped_refptr<Extension> extension; |
| extension = LoadExtension( |
| "filebrowser_url_override.json", |
| &error, |
| Extension::COMPONENT, |
| Extension::STRICT_ERROR_CHECKS); |
| #if defined(FILE_MANAGER_EXTENSION) |
| EXPECT_EQ("", error); |
| #else |
| EXPECT_EQ(std::string(errors::kInvalidChromeURLOverrides), error); |
| #endif |
| |
| // Extensions of other types can't ovverride chrome://files/ URL. |
| LoadAndExpectError("filebrowser_url_override.json", |
| errors::kInvalidChromeURLOverrides); |
| } |
| |
| TEST_F(ExtensionManifestTest, OfflineEnabled) { |
| LoadAndExpectError("offline_enabled_invalid.json", |
| errors::kInvalidOfflineEnabled); |
| scoped_refptr<Extension> extension_0( |
| LoadAndExpectSuccess("offline_enabled_extension.json")); |
| EXPECT_TRUE(extension_0->offline_enabled()); |
| scoped_refptr<Extension> extension_1( |
| LoadAndExpectSuccess("offline_enabled_packaged_app.json")); |
| EXPECT_TRUE(extension_1->offline_enabled()); |
| scoped_refptr<Extension> extension_2( |
| LoadAndExpectSuccess("offline_disabled_packaged_app.json")); |
| EXPECT_FALSE(extension_2->offline_enabled()); |
| scoped_refptr<Extension> extension_3( |
| LoadAndExpectSuccess("offline_default_packaged_app.json")); |
| EXPECT_FALSE(extension_3->offline_enabled()); |
| scoped_refptr<Extension> extension_4( |
| LoadAndExpectSuccess("offline_enabled_hosted_app.json")); |
| EXPECT_TRUE(extension_4->offline_enabled()); |
| } |
| |
| TEST_F(ExtensionManifestTest, PlatformAppOnlyPermissions) { |
| ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); |
| ExtensionAPIPermissionSet private_perms; |
| private_perms.insert(ExtensionAPIPermission::kSocket); |
| |
| ExtensionAPIPermissionSet perms = info->GetAll(); |
| int count = 0; |
| int platform_app = ExtensionAPIPermission::kTypePlatformApp; |
| for (ExtensionAPIPermissionSet::iterator i = perms.begin(); |
| i != perms.end(); ++i) { |
| count += private_perms.count(*i); |
| EXPECT_EQ(private_perms.count(*i) > 0, |
| info->GetByID(*i)->type_restrictions() == platform_app); |
| } |
| EXPECT_EQ(1, count); |
| |
| // This guy should fail to load because he's requesting platform-app-only |
| // permissions. |
| LoadAndExpectError("evil_non_platform_app.json", |
| errors::kPermissionNotAllowed, |
| Extension::INTERNAL, Extension::STRICT_ERROR_CHECKS); |
| |
| // This guy is identical to the previous but doesn't ask for any |
| // platform-app-only permissions. We should be able to load him and ask |
| // questions about his permissions. |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("not_platform_app.json")); |
| ExtensionAPIPermissionSet apis = extension->GetActivePermissions()->apis(); |
| for (ExtensionAPIPermissionSet::const_iterator i = apis.begin(); |
| i != apis.end(); ++i) |
| EXPECT_NE(platform_app, info->GetByID(*i)->type_restrictions()); |
| } |
| |
| TEST_F(ExtensionManifestTest, BackgroundPage) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("background_page.json")); |
| ASSERT_TRUE(extension); |
| EXPECT_EQ("/foo.html", extension->GetBackgroundURL().path()); |
| |
| std::string error; |
| scoped_ptr<DictionaryValue> manifest( |
| LoadManifestFile("background_page_legacy.json", &error)); |
| ASSERT_TRUE(manifest.get()); |
| extension = LoadAndExpectSuccess(Manifest(manifest.get(), "")); |
| ASSERT_TRUE(extension); |
| EXPECT_EQ("/foo.html", extension->GetBackgroundURL().path()); |
| |
| manifest->SetInteger(keys::kManifestVersion, 2); |
| extension = LoadAndExpectSuccess(Manifest(manifest.get(), "")); |
| ASSERT_TRUE(extension); |
| EXPECT_FALSE(extension->GetBackgroundURL().is_valid()); |
| } |
| |
| TEST_F(ExtensionManifestTest, BackgroundScripts) { |
| std::string error; |
| scoped_ptr<DictionaryValue> manifest( |
| LoadManifestFile("background_scripts.json", &error)); |
| ASSERT_TRUE(manifest.get()); |
| |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess(Manifest(manifest.get(), ""))); |
| ASSERT_TRUE(extension); |
| EXPECT_EQ(2u, extension->background_scripts().size()); |
| EXPECT_EQ("foo.js", extension->background_scripts()[0u]); |
| EXPECT_EQ("bar/baz.js", extension->background_scripts()[1u]); |
| |
| EXPECT_TRUE(extension->has_background_page()); |
| EXPECT_EQ(std::string("/") + |
| extension_filenames::kGeneratedBackgroundPageFilename, |
| extension->GetBackgroundURL().path()); |
| |
| manifest->SetString("background_page", "monkey.html"); |
| LoadAndExpectError(Manifest(manifest.get(), ""), |
| errors::kInvalidBackgroundCombination); |
| } |
| |
| TEST_F(ExtensionManifestTest, PageActionManifestVersion2) { |
| scoped_refptr<Extension> extension( |
| LoadAndExpectSuccess("page_action_manifest_version_2.json")); |
| ASSERT_TRUE(extension.get()); |
| ASSERT_TRUE(extension->page_action()); |
| |
| EXPECT_EQ("", extension->page_action()->id()); |
| EXPECT_EQ(0u, extension->page_action()->icon_paths()->size()); |
| EXPECT_EQ("", extension->page_action()->GetTitle( |
| ExtensionAction::kDefaultTabId)); |
| EXPECT_FALSE(extension->page_action()->HasPopup( |
| ExtensionAction::kDefaultTabId)); |
| |
| LoadAndExpectError("page_action_manifest_version_2b.json", |
| errors::kInvalidPageActionPopup); |
| } |