Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/memory/scoped_ptr.h"
      9 #include "base/path_service.h"
     10 #include "base/string_number_conversions.h"
     11 #include "base/string_util.h"
     12 #include "base/utf_string_conversions.h"
     13 #include "chrome/common/chrome_paths.h"
     14 #include "chrome/common/chrome_switches.h"
     15 #include "chrome/common/extensions/extension.h"
     16 #include "chrome/common/extensions/extension_constants.h"
     17 #include "chrome/common/extensions/extension_error_utils.h"
     18 #include "chrome/common/extensions/extension_sidebar_defaults.h"
     19 #include "chrome/common/extensions/file_browser_handler.h"
     20 #include "chrome/common/extensions/url_pattern.h"
     21 #include "content/common/json_value_serializer.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 namespace errors = extension_manifest_errors;
     25 namespace keys = extension_manifest_keys;
     26 
     27 class ExtensionManifestTest : public testing::Test {
     28  public:
     29   ExtensionManifestTest() : enable_apps_(true) {}
     30 
     31  protected:
     32   DictionaryValue* LoadManifestFile(const std::string& filename,
     33                                     std::string* error) {
     34     FilePath path;
     35     PathService::Get(chrome::DIR_TEST_DATA, &path);
     36     path = path.AppendASCII("extensions")
     37         .AppendASCII("manifest_tests")
     38         .AppendASCII(filename.c_str());
     39     EXPECT_TRUE(file_util::PathExists(path));
     40 
     41     JSONFileValueSerializer serializer(path);
     42     return static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error));
     43   }
     44 
     45   scoped_refptr<Extension> LoadExtensionWithLocation(
     46       DictionaryValue* value,
     47       Extension::Location location,
     48       bool strict_error_checks,
     49       std::string* error) {
     50     FilePath path;
     51     PathService::Get(chrome::DIR_TEST_DATA, &path);
     52     path = path.AppendASCII("extensions").AppendASCII("manifest_tests");
     53     int flags = Extension::NO_FLAGS;
     54     if (strict_error_checks)
     55       flags |= Extension::STRICT_ERROR_CHECKS;
     56     return Extension::Create(path.DirName(), location, *value, flags, error);
     57   }
     58 
     59   scoped_refptr<Extension> LoadExtension(const std::string& name,
     60                                          std::string* error) {
     61     return LoadExtensionWithLocation(name, Extension::INTERNAL, false, error);
     62   }
     63 
     64   scoped_refptr<Extension> LoadExtensionStrict(const std::string& name,
     65                                                std::string* error) {
     66     return LoadExtensionWithLocation(name, Extension::INTERNAL, true, error);
     67   }
     68 
     69   scoped_refptr<Extension> LoadExtension(DictionaryValue* value,
     70                                          std::string* error) {
     71     // Loading as an installed extension disables strict error checks.
     72     return LoadExtensionWithLocation(value, Extension::INTERNAL, false, error);
     73   }
     74 
     75   scoped_refptr<Extension> LoadExtensionWithLocation(
     76       const std::string& name,
     77       Extension::Location location,
     78       bool strict_error_checks,
     79       std::string* error) {
     80     scoped_ptr<DictionaryValue> value(LoadManifestFile(name, error));
     81     if (!value.get())
     82       return NULL;
     83     return LoadExtensionWithLocation(value.get(), location,
     84                                      strict_error_checks, error);
     85   }
     86 
     87   scoped_refptr<Extension> LoadAndExpectSuccess(const std::string& name) {
     88     std::string error;
     89     scoped_refptr<Extension> extension = LoadExtension(name, &error);
     90     EXPECT_TRUE(extension) << name;
     91     EXPECT_EQ("", error) << name;
     92     return extension;
     93   }
     94 
     95   scoped_refptr<Extension> LoadStrictAndExpectSuccess(const std::string& name) {
     96     std::string error;
     97     scoped_refptr<Extension> extension = LoadExtensionStrict(name, &error);
     98     EXPECT_TRUE(extension) << name;
     99     EXPECT_EQ("", error) << name;
    100     return extension;
    101   }
    102 
    103   scoped_refptr<Extension> LoadAndExpectSuccess(DictionaryValue* manifest,
    104                                                 const std::string& name) {
    105     std::string error;
    106     scoped_refptr<Extension> extension = LoadExtension(manifest, &error);
    107     EXPECT_TRUE(extension) << "Unexpected success for " << name;
    108     EXPECT_EQ("", error) << "Unexpected no error for " << name;
    109     return extension;
    110   }
    111 
    112   void VerifyExpectedError(Extension* extension,
    113                            const std::string& name,
    114                            const std::string& error,
    115                            const std::string& expected_error) {
    116     EXPECT_FALSE(extension) <<
    117         "Expected failure loading extension '" << name <<
    118         "', but didn't get one.";
    119     EXPECT_TRUE(MatchPattern(error, expected_error)) << name <<
    120         " expected '" << expected_error << "' but got '" << error << "'";
    121   }
    122 
    123   void LoadAndExpectError(const std::string& name,
    124                           const std::string& expected_error) {
    125     std::string error;
    126     scoped_refptr<Extension> extension(LoadExtension(name, &error));
    127     VerifyExpectedError(extension.get(), name, error, expected_error);
    128   }
    129 
    130   void LoadAndExpectErrorStrict(const std::string& name,
    131                                 const std::string& expected_error) {
    132     std::string error;
    133     scoped_refptr<Extension> extension(LoadExtensionStrict(name, &error));
    134     VerifyExpectedError(extension.get(), name, error, expected_error);
    135   }
    136 
    137   void LoadAndExpectError(DictionaryValue* manifest,
    138                           const std::string& name,
    139                           const std::string& expected_error) {
    140     std::string error;
    141     scoped_refptr<Extension> extension(LoadExtension(manifest, &error));
    142     VerifyExpectedError(extension.get(), name, error, expected_error);
    143   }
    144 
    145   bool enable_apps_;
    146 };
    147 
    148 TEST_F(ExtensionManifestTest, ValidApp) {
    149   scoped_refptr<Extension> extension(LoadAndExpectSuccess("valid_app.json"));
    150   ASSERT_EQ(2u, extension->web_extent().patterns().size());
    151   EXPECT_EQ("http://www.google.com/mail/*",
    152             extension->web_extent().patterns()[0].GetAsString());
    153   EXPECT_EQ("http://www.google.com/foobar/*",
    154             extension->web_extent().patterns()[1].GetAsString());
    155   EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container());
    156   EXPECT_EQ("http://www.google.com/mail/", extension->launch_web_url());
    157 }
    158 
    159 TEST_F(ExtensionManifestTest, AppWebUrls) {
    160   LoadAndExpectError("web_urls_wrong_type.json",
    161                      errors::kInvalidWebURLs);
    162   LoadAndExpectError(
    163       "web_urls_invalid_1.json",
    164       ExtensionErrorUtils::FormatErrorMessage(
    165           errors::kInvalidWebURL,
    166           base::IntToString(0),
    167           errors::kExpectString));
    168 
    169   LoadAndExpectError(
    170       "web_urls_invalid_2.json",
    171       ExtensionErrorUtils::FormatErrorMessage(
    172           errors::kInvalidWebURL,
    173           base::IntToString(0),
    174           URLPattern::GetParseResultString(
    175               URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR)));
    176 
    177   LoadAndExpectError(
    178       "web_urls_invalid_3.json",
    179       ExtensionErrorUtils::FormatErrorMessage(
    180           errors::kInvalidWebURL,
    181           base::IntToString(0),
    182           errors::kNoWildCardsInPaths));
    183 
    184   LoadAndExpectError(
    185       "web_urls_invalid_4.json",
    186       ExtensionErrorUtils::FormatErrorMessage(
    187           errors::kInvalidWebURL,
    188           base::IntToString(0),
    189           errors::kCannotClaimAllURLsInExtent));
    190 
    191   LoadAndExpectError(
    192       "web_urls_invalid_5.json",
    193       ExtensionErrorUtils::FormatErrorMessage(
    194           errors::kInvalidWebURL,
    195           base::IntToString(1),
    196           errors::kCannotClaimAllHostsInExtent));
    197 
    198   // Ports in app.urls only raise an error when loading as a
    199   // developer would.
    200   LoadAndExpectSuccess("web_urls_invalid_has_port.json");
    201   LoadAndExpectErrorStrict(
    202       "web_urls_invalid_has_port.json",
    203       ExtensionErrorUtils::FormatErrorMessage(
    204           errors::kInvalidWebURL,
    205           base::IntToString(1),
    206           URLPattern::GetParseResultString(URLPattern::PARSE_ERROR_HAS_COLON)));
    207 
    208 
    209   scoped_refptr<Extension> extension(
    210       LoadAndExpectSuccess("web_urls_default.json"));
    211   ASSERT_EQ(1u, extension->web_extent().patterns().size());
    212   EXPECT_EQ("*://www.google.com/*",
    213             extension->web_extent().patterns()[0].GetAsString());
    214 }
    215 
    216 TEST_F(ExtensionManifestTest, AppLaunchContainer) {
    217   scoped_refptr<Extension> extension;
    218 
    219   extension = LoadAndExpectSuccess("launch_tab.json");
    220   EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container());
    221 
    222   extension = LoadAndExpectSuccess("launch_panel.json");
    223   EXPECT_EQ(extension_misc::LAUNCH_PANEL, extension->launch_container());
    224 
    225   extension = LoadAndExpectSuccess("launch_default.json");
    226   EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container());
    227 
    228   extension = LoadAndExpectSuccess("launch_width.json");
    229   EXPECT_EQ(640, extension->launch_width());
    230 
    231   extension = LoadAndExpectSuccess("launch_height.json");
    232   EXPECT_EQ(480, extension->launch_height());
    233 
    234   LoadAndExpectError("launch_window.json",
    235                      errors::kInvalidLaunchContainer);
    236   LoadAndExpectError("launch_container_invalid_type.json",
    237                      errors::kInvalidLaunchContainer);
    238   LoadAndExpectError("launch_container_invalid_value.json",
    239                      errors::kInvalidLaunchContainer);
    240   LoadAndExpectError("launch_container_without_launch_url.json",
    241                      errors::kLaunchURLRequired);
    242   LoadAndExpectError("launch_width_invalid.json",
    243                      errors::kInvalidLaunchWidthContainer);
    244   LoadAndExpectError("launch_width_negative.json",
    245                      errors::kInvalidLaunchWidth);
    246   LoadAndExpectError("launch_height_invalid.json",
    247                      errors::kInvalidLaunchHeightContainer);
    248   LoadAndExpectError("launch_height_negative.json",
    249                      errors::kInvalidLaunchHeight);
    250 }
    251 
    252 TEST_F(ExtensionManifestTest, AppLaunchURL) {
    253   LoadAndExpectError("launch_path_and_url.json",
    254                      errors::kLaunchPathAndURLAreExclusive);
    255   LoadAndExpectError("launch_path_invalid_type.json",
    256                      errors::kInvalidLaunchLocalPath);
    257   LoadAndExpectError("launch_path_invalid_value.json",
    258                      errors::kInvalidLaunchLocalPath);
    259   LoadAndExpectError("launch_url_invalid_type_1.json",
    260                      errors::kInvalidLaunchWebURL);
    261   LoadAndExpectError("launch_url_invalid_type_2.json",
    262                      errors::kInvalidLaunchWebURL);
    263   LoadAndExpectError("launch_url_invalid_type_3.json",
    264                      errors::kInvalidLaunchWebURL);
    265 
    266   scoped_refptr<Extension> extension;
    267   extension = LoadAndExpectSuccess("launch_local_path.json");
    268   EXPECT_EQ(extension->url().spec() + "launch.html",
    269             extension->GetFullLaunchURL().spec());
    270 
    271   LoadAndExpectError("launch_web_url_relative.json",
    272                      errors::kInvalidLaunchWebURL);
    273 
    274   extension = LoadAndExpectSuccess("launch_web_url_absolute.json");
    275   EXPECT_EQ(GURL("http://www.google.com/launch.html"),
    276             extension->GetFullLaunchURL());
    277 }
    278 
    279 TEST_F(ExtensionManifestTest, Override) {
    280   LoadAndExpectError("override_newtab_and_history.json",
    281                      errors::kMultipleOverrides);
    282   LoadAndExpectError("override_invalid_page.json",
    283                      errors::kInvalidChromeURLOverrides);
    284 
    285   scoped_refptr<Extension> extension;
    286 
    287   extension = LoadAndExpectSuccess("override_new_tab.json");
    288   EXPECT_EQ(extension->url().spec() + "newtab.html",
    289             extension->GetChromeURLOverrides().find("newtab")->second.spec());
    290 
    291   extension = LoadAndExpectSuccess("override_history.json");
    292   EXPECT_EQ(extension->url().spec() + "history.html",
    293             extension->GetChromeURLOverrides().find("history")->second.spec());
    294 }
    295 
    296 TEST_F(ExtensionManifestTest, ChromeURLPermissionInvalid) {
    297   LoadAndExpectError("permission_chrome_url_invalid.json",
    298       errors::kInvalidPermissionScheme);
    299 }
    300 
    301 TEST_F(ExtensionManifestTest, ChromeResourcesPermissionValidOnlyForComponents) {
    302   LoadAndExpectError("permission_chrome_resources_url.json",
    303       errors::kInvalidPermissionScheme);
    304   std::string error;
    305   scoped_refptr<Extension> extension;
    306   extension = LoadExtensionWithLocation(
    307       "permission_chrome_resources_url.json",
    308       Extension::COMPONENT,
    309       true,  // Strict error checking
    310       &error);
    311   EXPECT_EQ("", error);
    312 }
    313 
    314 TEST_F(ExtensionManifestTest, InvalidContentScriptMatchPattern) {
    315 
    316   // chrome:// urls are not allowed.
    317   LoadAndExpectError(
    318       "content_script_chrome_url_invalid.json",
    319       ExtensionErrorUtils::FormatErrorMessage(
    320           errors::kInvalidMatch,
    321           base::IntToString(0),
    322           base::IntToString(0),
    323           URLPattern::GetParseResultString(
    324               URLPattern::PARSE_ERROR_INVALID_SCHEME)));
    325 
    326   // Match paterns must be strings.
    327   LoadAndExpectError(
    328       "content_script_match_pattern_not_string.json",
    329       ExtensionErrorUtils::FormatErrorMessage(
    330           errors::kInvalidMatch,
    331           base::IntToString(0),
    332           base::IntToString(0),
    333           errors::kExpectString));
    334 
    335   // Ports in match patterns cause an error, but only when loading
    336   // in developer mode.
    337   LoadAndExpectSuccess("forbid_ports_in_content_scripts.json");
    338 
    339   // Loading as a developer would should give an error.
    340   LoadAndExpectErrorStrict(
    341       "forbid_ports_in_content_scripts.json",
    342       ExtensionErrorUtils::FormatErrorMessage(
    343           errors::kInvalidMatch,
    344           base::IntToString(1),
    345           base::IntToString(0),
    346           URLPattern::GetParseResultString(
    347               URLPattern::PARSE_ERROR_HAS_COLON)));
    348 }
    349 
    350 TEST_F(ExtensionManifestTest, ExperimentalPermission) {
    351   LoadAndExpectError("experimental.json", errors::kExperimentalFlagRequired);
    352   CommandLine old_command_line = *CommandLine::ForCurrentProcess();
    353   CommandLine::ForCurrentProcess()->AppendSwitch(
    354       switches::kEnableExperimentalExtensionApis);
    355   LoadAndExpectSuccess("experimental.json");
    356   *CommandLine::ForCurrentProcess() = old_command_line;
    357 }
    358 
    359 TEST_F(ExtensionManifestTest, DevToolsExtensions) {
    360   LoadAndExpectError("devtools_extension_no_permissions.json",
    361       errors::kDevToolsExperimental);
    362   LoadAndExpectError("devtools_extension_url_invalid_type.json",
    363       errors::kInvalidDevToolsPage);
    364 
    365   CommandLine old_command_line = *CommandLine::ForCurrentProcess();
    366   CommandLine::ForCurrentProcess()->AppendSwitch(
    367       switches::kEnableExperimentalExtensionApis);
    368 
    369   scoped_refptr<Extension> extension;
    370   extension = LoadAndExpectSuccess("devtools_extension.json");
    371   EXPECT_EQ(extension->url().spec() + "devtools.html",
    372             extension->devtools_url().spec());
    373   EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
    374 
    375   *CommandLine::ForCurrentProcess() = old_command_line;
    376 }
    377 
    378 TEST_F(ExtensionManifestTest, Sidebar) {
    379   LoadAndExpectError("sidebar.json",
    380       errors::kExperimentalFlagRequired);
    381 
    382   CommandLine old_command_line = *CommandLine::ForCurrentProcess();
    383   CommandLine::ForCurrentProcess()->AppendSwitch(
    384       switches::kEnableExperimentalExtensionApis);
    385 
    386   LoadAndExpectError("sidebar_no_permissions.json",
    387       errors::kSidebarExperimental);
    388 
    389   LoadAndExpectError("sidebar_icon_empty.json",
    390       errors::kInvalidSidebarDefaultIconPath);
    391   LoadAndExpectError("sidebar_icon_invalid_type.json",
    392       errors::kInvalidSidebarDefaultIconPath);
    393   LoadAndExpectError("sidebar_page_empty.json",
    394       errors::kInvalidSidebarDefaultPage);
    395   LoadAndExpectError("sidebar_page_invalid_type.json",
    396       errors::kInvalidSidebarDefaultPage);
    397   LoadAndExpectError("sidebar_title_invalid_type.json",
    398       errors::kInvalidSidebarDefaultTitle);
    399 
    400   scoped_refptr<Extension> extension(LoadAndExpectSuccess("sidebar.json"));
    401   ASSERT_TRUE(extension->sidebar_defaults() != NULL);
    402   EXPECT_EQ(extension->sidebar_defaults()->default_title(),
    403             ASCIIToUTF16("Default title"));
    404   EXPECT_EQ(extension->sidebar_defaults()->default_icon_path(),
    405             "icon.png");
    406   EXPECT_EQ(extension->url().spec() + "sidebar.html",
    407             extension->sidebar_defaults()->default_page().spec());
    408 
    409   *CommandLine::ForCurrentProcess() = old_command_line;
    410 }
    411 
    412 TEST_F(ExtensionManifestTest, DisallowHybridApps) {
    413   LoadAndExpectError("disallow_hybrid_1.json",
    414       ExtensionErrorUtils::FormatErrorMessage(
    415           errors::kHostedAppsCannotIncludeExtensionFeatures,
    416           keys::kBrowserAction));
    417   LoadAndExpectError("disallow_hybrid_2.json",
    418                      errors::kBackgroundPermissionNeeded);
    419 }
    420 
    421 TEST_F(ExtensionManifestTest, OptionsPageInApps) {
    422   scoped_refptr<Extension> extension;
    423 
    424   // Allow options page with absolute URL in hosed apps.
    425   extension = LoadAndExpectSuccess("hosted_app_absolute_options.json");
    426   EXPECT_STREQ("http",
    427                extension->options_url().scheme().c_str());
    428   EXPECT_STREQ("example.com",
    429                extension->options_url().host().c_str());
    430   EXPECT_STREQ("options.html",
    431                extension->options_url().ExtractFileName().c_str());
    432 
    433   // Forbid options page with relative URL in hosted apps.
    434   LoadAndExpectError("hosted_app_relative_options.json",
    435                      errors::kInvalidOptionsPageInHostedApp);
    436 
    437   // Forbid options page with non-(http|https) scheme in hosted app.
    438   LoadAndExpectError("hosted_app_file_options.json",
    439                      errors::kInvalidOptionsPageInHostedApp);
    440 
    441   // Forbid absolute URL for options page in packaged apps.
    442   LoadAndExpectError("packaged_app_absolute_options.json",
    443                      errors::kInvalidOptionsPageExpectUrlInPackage);
    444 }
    445 
    446 TEST_F(ExtensionManifestTest, AllowUnrecognizedPermissions) {
    447   std::string error;
    448   scoped_ptr<DictionaryValue> manifest(
    449       LoadManifestFile("valid_app.json", &error));
    450   ASSERT_TRUE(manifest.get());
    451 
    452   ListValue *permissions = new ListValue();
    453   manifest->Set(keys::kPermissions, permissions);
    454   for (size_t i = 0; i < Extension::kNumPermissions; i++) {
    455     const char* name = Extension::kPermissions[i].name;
    456     StringValue* p = new StringValue(name);
    457     permissions->Clear();
    458     permissions->Append(p);
    459     std::string message_name = base::StringPrintf("permission-%s", name);
    460 
    461     // Extensions are allowed to contain unrecognized API permissions,
    462     // so there shouldn't be any errors.
    463     scoped_refptr<Extension> extension;
    464     extension = LoadAndExpectSuccess(manifest.get(), message_name);
    465   }
    466 }
    467 
    468 TEST_F(ExtensionManifestTest, NormalizeIconPaths) {
    469   scoped_refptr<Extension> extension(
    470       LoadAndExpectSuccess("normalize_icon_paths.json"));
    471   EXPECT_EQ("16.png",
    472             extension->icons().Get(16, ExtensionIconSet::MATCH_EXACTLY));
    473   EXPECT_EQ("48.png",
    474             extension->icons().Get(48, ExtensionIconSet::MATCH_EXACTLY));
    475 }
    476 
    477 TEST_F(ExtensionManifestTest, DisallowMultipleUISurfaces) {
    478   LoadAndExpectError("multiple_ui_surfaces_1.json", errors::kOneUISurfaceOnly);
    479   LoadAndExpectError("multiple_ui_surfaces_2.json", errors::kOneUISurfaceOnly);
    480   LoadAndExpectError("multiple_ui_surfaces_3.json", errors::kOneUISurfaceOnly);
    481 }
    482 
    483 TEST_F(ExtensionManifestTest, ParseHomepageURLs) {
    484   scoped_refptr<Extension> extension(
    485       LoadAndExpectSuccess("homepage_valid.json"));
    486   LoadAndExpectError("homepage_empty.json",
    487                      extension_manifest_errors::kInvalidHomepageURL);
    488   LoadAndExpectError("homepage_invalid.json",
    489                      extension_manifest_errors::kInvalidHomepageURL);
    490 }
    491 
    492 TEST_F(ExtensionManifestTest, GetHomepageURL) {
    493   scoped_refptr<Extension> extension(
    494       LoadAndExpectSuccess("homepage_valid.json"));
    495   EXPECT_EQ(GURL("http://foo.com#bar"), extension->GetHomepageURL());
    496 
    497   // The Google Gallery URL ends with the id, which depends on the path, which
    498   // can be different in testing, so we just check the part before id.
    499   extension = LoadAndExpectSuccess("homepage_google_hosted.json");
    500   EXPECT_TRUE(StartsWithASCII(extension->GetHomepageURL().spec(),
    501                               "https://chrome.google.com/webstore/detail/",
    502                               false));
    503 
    504   extension = LoadAndExpectSuccess("homepage_externally_hosted.json");
    505   EXPECT_EQ(GURL(), extension->GetHomepageURL());
    506 }
    507 
    508 TEST_F(ExtensionManifestTest, DefaultPathForExtent) {
    509   scoped_refptr<Extension> extension(
    510       LoadAndExpectSuccess("default_path_for_extent.json"));
    511 
    512   ASSERT_EQ(1u, extension->web_extent().patterns().size());
    513   EXPECT_EQ("/*", extension->web_extent().patterns()[0].path());
    514   EXPECT_TRUE(extension->web_extent().ContainsURL(
    515       GURL("http://www.google.com/monkey")));
    516 }
    517 
    518 TEST_F(ExtensionManifestTest, DefaultLocale) {
    519   LoadAndExpectError("default_locale_invalid.json",
    520                      extension_manifest_errors::kInvalidDefaultLocale);
    521 
    522   scoped_refptr<Extension> extension(
    523       LoadAndExpectSuccess("default_locale_valid.json"));
    524   EXPECT_EQ("de-AT", extension->default_locale());
    525 }
    526 
    527 TEST_F(ExtensionManifestTest, TtsProvider) {
    528   LoadAndExpectError("tts_provider_invalid_1.json",
    529                      extension_manifest_errors::kInvalidTts);
    530   LoadAndExpectError("tts_provider_invalid_2.json",
    531                      extension_manifest_errors::kInvalidTtsVoices);
    532   LoadAndExpectError("tts_provider_invalid_3.json",
    533                      extension_manifest_errors::kInvalidTtsVoices);
    534   LoadAndExpectError("tts_provider_invalid_4.json",
    535                      extension_manifest_errors::kInvalidTtsVoicesVoiceName);
    536   LoadAndExpectError("tts_provider_invalid_5.json",
    537                      extension_manifest_errors::kInvalidTtsVoicesLocale);
    538   LoadAndExpectError("tts_provider_invalid_6.json",
    539                      extension_manifest_errors::kInvalidTtsVoicesLocale);
    540   LoadAndExpectError("tts_provider_invalid_7.json",
    541                      extension_manifest_errors::kInvalidTtsVoicesGender);
    542 
    543   scoped_refptr<Extension> extension(
    544       LoadAndExpectSuccess("tts_provider_valid.json"));
    545 
    546   ASSERT_EQ(1u, extension->tts_voices().size());
    547   EXPECT_EQ("name", extension->tts_voices()[0].voice_name);
    548   EXPECT_EQ("en-US", extension->tts_voices()[0].locale);
    549   EXPECT_EQ("female", extension->tts_voices()[0].gender);
    550 }
    551 
    552 TEST_F(ExtensionManifestTest, ForbidPortsInPermissions) {
    553   // Loading as a user would shoud not trigger an error.
    554   LoadAndExpectSuccess("forbid_ports_in_permissions.json");
    555 
    556   // Ideally, loading as a developer would give an error.
    557   // To ensure that we do not error out on a valid permission
    558   // in a future version of chrome, validation is to loose
    559   // to flag this case.
    560   LoadStrictAndExpectSuccess("forbid_ports_in_permissions.json");
    561 }
    562 
    563 TEST_F(ExtensionManifestTest, IsolatedApps) {
    564   // Requires --enable-experimental-app-manifests
    565   scoped_refptr<Extension> extension(
    566       LoadAndExpectSuccess("isolated_app_valid.json"));
    567   EXPECT_FALSE(extension->is_storage_isolated());
    568 
    569   CommandLine old_command_line = *CommandLine::ForCurrentProcess();
    570   CommandLine::ForCurrentProcess()->AppendSwitch(
    571       switches::kEnableExperimentalAppManifests);
    572   scoped_refptr<Extension> extension2(
    573       LoadAndExpectSuccess("isolated_app_valid.json"));
    574   EXPECT_TRUE(extension2->is_storage_isolated());
    575   *CommandLine::ForCurrentProcess() = old_command_line;
    576 }
    577 
    578 
    579 TEST_F(ExtensionManifestTest, FileBrowserHandlers) {
    580   LoadAndExpectError("filebrowser_invalid_actions_1.json",
    581       errors::kInvalidFileBrowserHandler);
    582   LoadAndExpectError("filebrowser_invalid_actions_2.json",
    583       errors::kInvalidFileBrowserHandler);
    584   LoadAndExpectError("filebrowser_invalid_action_id.json",
    585       errors::kInvalidPageActionId);
    586   LoadAndExpectError("filebrowser_invalid_action_title.json",
    587       errors::kInvalidPageActionDefaultTitle);
    588   LoadAndExpectError("filebrowser_invalid_action_id.json",
    589       errors::kInvalidPageActionId);
    590   LoadAndExpectError("filebrowser_invalid_file_filters_1.json",
    591       errors::kInvalidFileFiltersList);
    592   LoadAndExpectError("filebrowser_invalid_file_filters_2.json",
    593       ExtensionErrorUtils::FormatErrorMessage(
    594           errors::kInvalidFileFilterValue, base::IntToString(0)));
    595   LoadAndExpectError("filebrowser_invalid_file_filters_url.json",
    596       ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidURLPatternError,
    597                                               "http:*.html"));
    598 
    599   scoped_refptr<Extension> extension(
    600       LoadAndExpectSuccess("filebrowser_valid.json"));
    601   ASSERT_TRUE(extension->file_browser_handlers() != NULL);
    602   ASSERT_EQ(extension->file_browser_handlers()->size(), 1U);
    603   const FileBrowserHandler* action =
    604       extension->file_browser_handlers()->at(0).get();
    605   EXPECT_EQ(action->title(), "Default title");
    606   EXPECT_EQ(action->icon_path(), "icon.png");
    607   const FileBrowserHandler::PatternList& patterns = action->file_url_patterns();
    608   ASSERT_EQ(patterns.size(), 1U);
    609   ASSERT_TRUE(action->MatchesURL(
    610       GURL("filesystem:chrome-extension://foo/local/test.txt")));
    611 }
    612