普通文本  |  612行  |  24.03 KB

// Copyright (c) 2011 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 "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.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/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_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_sidebar_defaults.h"
#include "chrome/common/extensions/file_browser_handler.h"
#include "chrome/common/extensions/url_pattern.h"
#include "content/common/json_value_serializer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace errors = extension_manifest_errors;
namespace keys = extension_manifest_keys;

class ExtensionManifestTest : public testing::Test {
 public:
  ExtensionManifestTest() : enable_apps_(true) {}

 protected:
  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));

    JSONFileValueSerializer serializer(path);
    return static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error));
  }

  scoped_refptr<Extension> LoadExtensionWithLocation(
      DictionaryValue* value,
      Extension::Location location,
      bool strict_error_checks,
      std::string* error) {
    FilePath path;
    PathService::Get(chrome::DIR_TEST_DATA, &path);
    path = path.AppendASCII("extensions").AppendASCII("manifest_tests");
    int flags = Extension::NO_FLAGS;
    if (strict_error_checks)
      flags |= Extension::STRICT_ERROR_CHECKS;
    return Extension::Create(path.DirName(), location, *value, flags, error);
  }

  scoped_refptr<Extension> LoadExtension(const std::string& name,
                                         std::string* error) {
    return LoadExtensionWithLocation(name, Extension::INTERNAL, false, error);
  }

  scoped_refptr<Extension> LoadExtensionStrict(const std::string& name,
                                               std::string* error) {
    return LoadExtensionWithLocation(name, Extension::INTERNAL, true, error);
  }

  scoped_refptr<Extension> LoadExtension(DictionaryValue* value,
                                         std::string* error) {
    // Loading as an installed extension disables strict error checks.
    return LoadExtensionWithLocation(value, Extension::INTERNAL, false, error);
  }

  scoped_refptr<Extension> LoadExtensionWithLocation(
      const std::string& name,
      Extension::Location location,
      bool strict_error_checks,
      std::string* error) {
    scoped_ptr<DictionaryValue> value(LoadManifestFile(name, error));
    if (!value.get())
      return NULL;
    return LoadExtensionWithLocation(value.get(), location,
                                     strict_error_checks, error);
  }

  scoped_refptr<Extension> LoadAndExpectSuccess(const std::string& name) {
    std::string error;
    scoped_refptr<Extension> extension = LoadExtension(name, &error);
    EXPECT_TRUE(extension) << name;
    EXPECT_EQ("", error) << name;
    return extension;
  }

  scoped_refptr<Extension> LoadStrictAndExpectSuccess(const std::string& name) {
    std::string error;
    scoped_refptr<Extension> extension = LoadExtensionStrict(name, &error);
    EXPECT_TRUE(extension) << name;
    EXPECT_EQ("", error) << name;
    return extension;
  }

  scoped_refptr<Extension> LoadAndExpectSuccess(DictionaryValue* manifest,
                                                const std::string& name) {
    std::string error;
    scoped_refptr<Extension> extension = LoadExtension(manifest, &error);
    EXPECT_TRUE(extension) << "Unexpected success for " << name;
    EXPECT_EQ("", error) << "Unexpected no error for " << 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 std::string& name,
                          const std::string& expected_error) {
    std::string error;
    scoped_refptr<Extension> extension(LoadExtension(name, &error));
    VerifyExpectedError(extension.get(), name, error, expected_error);
  }

  void LoadAndExpectErrorStrict(const std::string& name,
                                const std::string& expected_error) {
    std::string error;
    scoped_refptr<Extension> extension(LoadExtensionStrict(name, &error));
    VerifyExpectedError(extension.get(), name, error, expected_error);
  }

  void LoadAndExpectError(DictionaryValue* manifest,
                          const std::string& name,
                          const std::string& expected_error) {
    std::string error;
    scoped_refptr<Extension> extension(LoadExtension(manifest, &error));
    VerifyExpectedError(extension.get(), name, error, expected_error);
  }

  bool enable_apps_;
};

TEST_F(ExtensionManifestTest, ValidApp) {
  scoped_refptr<Extension> extension(LoadAndExpectSuccess("valid_app.json"));
  ASSERT_EQ(2u, extension->web_extent().patterns().size());
  EXPECT_EQ("http://www.google.com/mail/*",
            extension->web_extent().patterns()[0].GetAsString());
  EXPECT_EQ("http://www.google.com/foobar/*",
            extension->web_extent().patterns()[1].GetAsString());
  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));

  // Ports in app.urls only raise an error when loading as a
  // developer would.
  LoadAndExpectSuccess("web_urls_invalid_has_port.json");
  LoadAndExpectErrorStrict(
      "web_urls_invalid_has_port.json",
      ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidWebURL,
          base::IntToString(1),
          URLPattern::GetParseResultString(URLPattern::PARSE_ERROR_HAS_COLON)));


  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()[0].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, AppLaunchURL) {
  LoadAndExpectError("launch_path_and_url.json",
                     errors::kLaunchPathAndURLAreExclusive);
  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 = LoadExtensionWithLocation(
      "permission_chrome_resources_url.json",
      Extension::COMPONENT,
      true,  // Strict error checking
      &error);
  EXPECT_EQ("", error);
}

TEST_F(ExtensionManifestTest, InvalidContentScriptMatchPattern) {

  // 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));

  // Ports in match patterns cause an error, but only when loading
  // in developer mode.
  LoadAndExpectSuccess("forbid_ports_in_content_scripts.json");

  // Loading as a developer would should give an error.
  LoadAndExpectErrorStrict(
      "forbid_ports_in_content_scripts.json",
      ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidMatch,
          base::IntToString(1),
          base::IntToString(0),
          URLPattern::GetParseResultString(
              URLPattern::PARSE_ERROR_HAS_COLON)));
}

TEST_F(ExtensionManifestTest, ExperimentalPermission) {
  LoadAndExpectError("experimental.json", errors::kExperimentalFlagRequired);
  CommandLine old_command_line = *CommandLine::ForCurrentProcess();
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);
  LoadAndExpectSuccess("experimental.json");
  *CommandLine::ForCurrentProcess() = old_command_line;
}

TEST_F(ExtensionManifestTest, DevToolsExtensions) {
  LoadAndExpectError("devtools_extension_no_permissions.json",
      errors::kDevToolsExperimental);
  LoadAndExpectError("devtools_extension_url_invalid_type.json",
      errors::kInvalidDevToolsPage);

  CommandLine old_command_line = *CommandLine::ForCurrentProcess();
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);

  scoped_refptr<Extension> extension;
  extension = LoadAndExpectSuccess("devtools_extension.json");
  EXPECT_EQ(extension->url().spec() + "devtools.html",
            extension->devtools_url().spec());
  EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());

  *CommandLine::ForCurrentProcess() = old_command_line;
}

TEST_F(ExtensionManifestTest, Sidebar) {
  LoadAndExpectError("sidebar.json",
      errors::kExperimentalFlagRequired);

  CommandLine old_command_line = *CommandLine::ForCurrentProcess();
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);

  LoadAndExpectError("sidebar_no_permissions.json",
      errors::kSidebarExperimental);

  LoadAndExpectError("sidebar_icon_empty.json",
      errors::kInvalidSidebarDefaultIconPath);
  LoadAndExpectError("sidebar_icon_invalid_type.json",
      errors::kInvalidSidebarDefaultIconPath);
  LoadAndExpectError("sidebar_page_empty.json",
      errors::kInvalidSidebarDefaultPage);
  LoadAndExpectError("sidebar_page_invalid_type.json",
      errors::kInvalidSidebarDefaultPage);
  LoadAndExpectError("sidebar_title_invalid_type.json",
      errors::kInvalidSidebarDefaultTitle);

  scoped_refptr<Extension> extension(LoadAndExpectSuccess("sidebar.json"));
  ASSERT_TRUE(extension->sidebar_defaults() != NULL);
  EXPECT_EQ(extension->sidebar_defaults()->default_title(),
            ASCIIToUTF16("Default title"));
  EXPECT_EQ(extension->sidebar_defaults()->default_icon_path(),
            "icon.png");
  EXPECT_EQ(extension->url().spec() + "sidebar.html",
            extension->sidebar_defaults()->default_page().spec());

  *CommandLine::ForCurrentProcess() = old_command_line;
}

TEST_F(ExtensionManifestTest, DisallowHybridApps) {
  LoadAndExpectError("disallow_hybrid_1.json",
      ExtensionErrorUtils::FormatErrorMessage(
          errors::kHostedAppsCannotIncludeExtensionFeatures,
          keys::kBrowserAction));
  LoadAndExpectError("disallow_hybrid_2.json",
                     errors::kBackgroundPermissionNeeded);
}

TEST_F(ExtensionManifestTest, OptionsPageInApps) {
  scoped_refptr<Extension> extension;

  // Allow options page with absolute URL in hosed 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, AllowUnrecognizedPermissions) {
  std::string error;
  scoped_ptr<DictionaryValue> manifest(
      LoadManifestFile("valid_app.json", &error));
  ASSERT_TRUE(manifest.get());

  ListValue *permissions = new ListValue();
  manifest->Set(keys::kPermissions, permissions);
  for (size_t i = 0; i < Extension::kNumPermissions; i++) {
    const char* name = Extension::kPermissions[i].name;
    StringValue* p = new StringValue(name);
    permissions->Clear();
    permissions->Append(p);
    std::string message_name = base::StringPrintf("permission-%s", name);

    // Extensions are allowed to contain unrecognized API permissions,
    // so there shouldn't be any errors.
    scoped_refptr<Extension> extension;
    extension = LoadAndExpectSuccess(manifest.get(), message_name);
  }
}

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_1.json", errors::kOneUISurfaceOnly);
  LoadAndExpectError("multiple_ui_surfaces_2.json", errors::kOneUISurfaceOnly);
  LoadAndExpectError("multiple_ui_surfaces_3.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);
}

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()[0].path());
  EXPECT_TRUE(extension->web_extent().ContainsURL(
      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, TtsProvider) {
  LoadAndExpectError("tts_provider_invalid_1.json",
                     extension_manifest_errors::kInvalidTts);
  LoadAndExpectError("tts_provider_invalid_2.json",
                     extension_manifest_errors::kInvalidTtsVoices);
  LoadAndExpectError("tts_provider_invalid_3.json",
                     extension_manifest_errors::kInvalidTtsVoices);
  LoadAndExpectError("tts_provider_invalid_4.json",
                     extension_manifest_errors::kInvalidTtsVoicesVoiceName);
  LoadAndExpectError("tts_provider_invalid_5.json",
                     extension_manifest_errors::kInvalidTtsVoicesLocale);
  LoadAndExpectError("tts_provider_invalid_6.json",
                     extension_manifest_errors::kInvalidTtsVoicesLocale);
  LoadAndExpectError("tts_provider_invalid_7.json",
                     extension_manifest_errors::kInvalidTtsVoicesGender);

  scoped_refptr<Extension> extension(
      LoadAndExpectSuccess("tts_provider_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].locale);
  EXPECT_EQ("female", extension->tts_voices()[0].gender);
}

TEST_F(ExtensionManifestTest, ForbidPortsInPermissions) {
  // Loading as a user would shoud not trigger an error.
  LoadAndExpectSuccess("forbid_ports_in_permissions.json");

  // Ideally, loading as a developer would give an error.
  // To ensure that we do not error out on a valid permission
  // in a future version of chrome, validation is to loose
  // to flag this case.
  LoadStrictAndExpectSuccess("forbid_ports_in_permissions.json");
}

TEST_F(ExtensionManifestTest, IsolatedApps) {
  // Requires --enable-experimental-app-manifests
  scoped_refptr<Extension> extension(
      LoadAndExpectSuccess("isolated_app_valid.json"));
  EXPECT_FALSE(extension->is_storage_isolated());

  CommandLine old_command_line = *CommandLine::ForCurrentProcess();
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalAppManifests);
  scoped_refptr<Extension> extension2(
      LoadAndExpectSuccess("isolated_app_valid.json"));
  EXPECT_TRUE(extension2->is_storage_isolated());
  *CommandLine::ForCurrentProcess() = old_command_line;
}


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 FileBrowserHandler::PatternList& patterns = action->file_url_patterns();
  ASSERT_EQ(patterns.size(), 1U);
  ASSERT_TRUE(action->MatchesURL(
      GURL("filesystem:chrome-extension://foo/local/test.txt")));
}