diff options
author | cira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-19 20:45:14 +0000 |
---|---|---|
committer | cira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-19 20:45:14 +0000 |
commit | 300cc58db6bc8d78e279dc07c46f15d9702148c6 (patch) | |
tree | 01eb9d8585bc43b01850c226bc4d5f2a3ec00cea /chrome/browser | |
parent | cceaf1fbf3f4963c59a49268217f1e3edfb372ef (diff) | |
download | chromium_src-300cc58db6bc8d78e279dc07c46f15d9702148c6.zip chromium_src-300cc58db6bc8d78e279dc07c46f15d9702148c6.tar.gz chromium_src-300cc58db6bc8d78e279dc07c46f15d9702148c6.tar.bz2 |
This change enables Chrome to load locale information for the extension. It detects default locale, and filters out all locales not supported
by Chrome or with invalid names/missing messages.
It also checks for folders that start with _ and are not in the reserved list.
We don't validate messages file with this CL.
Added support for loading supplied locale information to the extension_file_util, and detecting default locale.
Added new constants to extension class (_locales directory name, messages filename).
Added new error messages to _constants.
Added new unittests.
BUG=12131
TEST=There should be no visible changes, except in case of error when loading extension (e.g. create empty _locales folder and try loading).
Review URL: http://codereview.chromium.org/170015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23739 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/extensions/extension_file_util.cc | 60 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_file_util.h | 7 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_file_util_unittest.cc | 109 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_l10n_util.cc | 90 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_l10n_util.h | 46 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_l10n_util_unittest.cc | 135 |
6 files changed, 447 insertions, 0 deletions
diff --git a/chrome/browser/extensions/extension_file_util.cc b/chrome/browser/extensions/extension_file_util.cc index 07ef77f..bf6cd9c 100644 --- a/chrome/browser/extensions/extension_file_util.cc +++ b/chrome/browser/extensions/extension_file_util.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/scoped_temp_dir.h" #include "base/string_util.h" +#include "chrome/browser/extensions/extension_l10n_util.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/json_value_serializer.h" @@ -276,6 +277,27 @@ Extension* LoadExtension(const FilePath& extension_path, bool require_key, } } + // Load locale information if available. + FilePath locale_path = extension_path.AppendASCII(Extension::kLocaleFolder); + if (file_util::PathExists(locale_path)) { + if (!extension_l10n_util::AddValidLocales(locale_path, + extension.get(), + error)) { + return NULL; + } + + if (!extension_l10n_util::ValidateDefaultLocale(extension.get())) { + *error = extension_manifest_errors::kLocalesNoDefaultLocaleSpecified; + return NULL; + } + } + + // Check children of extension root to see if any of them start with _ and is + // not on the reserved list. + if (!CheckForIllegalFilenames(extension_path, error)) { + return NULL; + } + return extension.release(); } @@ -349,4 +371,42 @@ void GarbageCollectExtensions(const FilePath& install_directory) { } } +bool CheckForIllegalFilenames(const FilePath& extension_path, + std::string* error) { + // Reserved underscore names. + static const char* reserved_names[] = { + Extension::kLocaleFolder + }; + static std::set<std::string> reserved_underscore_names( + reserved_names, reserved_names + arraysize(reserved_names)); + + // Enumerate all files and directories in the extension root. + // There is a problem when using pattern "_*" with FileEnumerator, so we have + // to cheat with find_first_of and match all. + file_util::FileEnumerator all_files( + extension_path, + false, + static_cast<file_util::FileEnumerator::FILE_TYPE>( + file_util::FileEnumerator::DIRECTORIES | + file_util::FileEnumerator::FILES)); + + FilePath files; + while (!(files = all_files.Next()).empty()) { + std::string filename = + WideToASCII(files.BaseName().ToWStringHack()); + // Skip all that don't start with "_". + if (filename.find_first_of("_") != 0) continue; + if (reserved_underscore_names.find(filename) == + reserved_underscore_names.end()) { + *error = StringPrintf( + "Cannot load extension with file or directory name %s." + "Filenames starting with \"_\" are reserved for use by the system", + filename.c_str()); + return false; + } + } + + return true; +} + } // extensionfile_util diff --git a/chrome/browser/extensions/extension_file_util.h b/chrome/browser/extensions/extension_file_util.h index 45d0b30..c2eb5c9 100644 --- a/chrome/browser/extensions/extension_file_util.h +++ b/chrome/browser/extensions/extension_file_util.h @@ -78,6 +78,13 @@ void UninstallExtension(const std::string& id, const FilePath& extensions_dir); // removing others? void GarbageCollectExtensions(const FilePath& extensions_dir); +// We need to reserve the namespace of entries that start with "_" for future +// use by Chrome. +// If any files or directories are found using "_" prefix and are not on +// reserved list we return false, and set error message. +bool CheckForIllegalFilenames(const FilePath& extension_path, + std::string* error); + } // extension_file_util #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_FILE_UTIL_H_ diff --git a/chrome/browser/extensions/extension_file_util_unittest.cc b/chrome/browser/extensions/extension_file_util_unittest.cc index 6e9b2dc..73dfb46 100644 --- a/chrome/browser/extensions/extension_file_util_unittest.cc +++ b/chrome/browser/extensions/extension_file_util_unittest.cc @@ -8,8 +8,13 @@ #include "base/scoped_temp_dir.h" #include "base/path_service.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/json_value_serializer.h" #include "testing/gtest/include/gtest/gtest.h" +namespace keys = extension_manifest_keys; + TEST(ExtensionFileUtil, MoveDirSafely) { // Create a test directory structure with some data in it. ScopedTempDir temp; @@ -162,6 +167,110 @@ TEST(ExtensionFileUtil, CompareToInstalledVersion) { ASSERT_EQ("", version_out); } +// Creates minimal manifest, with or without default_locale section. +bool CreateMinimalManifest(const std::string& locale, + const FilePath& manifest_path) { + DictionaryValue manifest; + + manifest.SetString(keys::kVersion, "1.0.0.0"); + manifest.SetString(keys::kName, "my extension"); + if (!locale.empty()) { + manifest.SetString(keys::kDefaultLocale, locale); + } + + JSONFileValueSerializer serializer(manifest_path); + return serializer.Serialize(manifest); +} + +TEST(ExtensionFileUtil, LoadExtensionWithValidLocales) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + ASSERT_TRUE(CreateMinimalManifest( + "en_US", temp.path().AppendASCII(Extension::kManifestFilename))); + + FilePath src_path = temp.path().AppendASCII(Extension::kLocaleFolder); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + FilePath locale_1 = src_path.AppendASCII("sr"); + ASSERT_TRUE(file_util::CreateDirectory(locale_1)); + + std::string data = "foobar"; + ASSERT_TRUE( + file_util::WriteFile(locale_1.AppendASCII(Extension::kMessagesFilename), + data.c_str(), data.length())); + + FilePath locale_2 = src_path.AppendASCII("en_US"); + ASSERT_TRUE(file_util::CreateDirectory(locale_2)); + + ASSERT_TRUE( + file_util::WriteFile(locale_2.AppendASCII(Extension::kMessagesFilename), + data.c_str(), data.length())); + + std::string error; + scoped_ptr<Extension> extension( + extension_file_util::LoadExtension(temp.path(), false, &error)); + ASSERT_FALSE(extension == NULL); + EXPECT_EQ(static_cast<unsigned int>(2), + extension->supported_locales().size()); + EXPECT_EQ("en-US", extension->default_locale()); +} + +TEST(ExtensionFileUtil, LoadExtensionWithoutLocalesFolder) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + ASSERT_TRUE(CreateMinimalManifest( + "", temp.path().AppendASCII(Extension::kManifestFilename))); + + std::string error; + scoped_ptr<Extension> extension( + extension_file_util::LoadExtension(temp.path(), false, &error)); + ASSERT_FALSE(extension == NULL); + EXPECT_TRUE(extension->supported_locales().empty()); + EXPECT_TRUE(extension->default_locale().empty()); +} + +TEST(ExtensionFileUtil, CheckIllegalFilenamesNoUnderscores) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + + FilePath src_path = temp.path().AppendASCII("some_dir"); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + std::string data = "foobar"; + ASSERT_TRUE(file_util::WriteFile(src_path.AppendASCII("some_file.txt"), + data.c_str(), data.length())); + std::string error; + EXPECT_TRUE(extension_file_util::CheckForIllegalFilenames(temp.path(), + &error)); +} + +TEST(ExtensionFileUtil, CheckIllegalFilenamesOnlyReserved) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + + FilePath src_path = temp.path().AppendASCII(Extension::kLocaleFolder); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + std::string error; + EXPECT_TRUE(extension_file_util::CheckForIllegalFilenames(temp.path(), + &error)); +} + +TEST(ExtensionFileUtil, CheckIllegalFilenamesReservedAndIllegal) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + + FilePath src_path = temp.path().AppendASCII(Extension::kLocaleFolder); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + src_path = temp.path().AppendASCII("_some_dir"); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + std::string error; + EXPECT_FALSE(extension_file_util::CheckForIllegalFilenames(temp.path(), + &error)); +} + // TODO(aa): More tests as motivation allows. Maybe steal some from // ExtensionsService? Many of them could probably be tested here without the // MessageLoop shenanigans. diff --git a/chrome/browser/extensions/extension_l10n_util.cc b/chrome/browser/extensions/extension_l10n_util.cc new file mode 100644 index 0000000..77fddba --- /dev/null +++ b/chrome/browser/extensions/extension_l10n_util.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2009 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/browser/extensions/extension_l10n_util.h" + +#include <set> +#include <string> +#include <vector> + +#include "app/l10n_util.h" +#include "base/file_util.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" + +namespace keys = extension_manifest_keys; + +namespace extension_l10n_util { + +bool ValidateDefaultLocale(const Extension* extension) { + std::string default_locale = extension->default_locale(); + + if (extension->supported_locales().find(default_locale) != + extension->supported_locales().end()) { + return true; + } else { + return false; + } +} + + +bool AddLocale(const std::set<std::string>& chrome_locales, + const FilePath& locale_folder, + Extension* extension, + std::string* locale_name, + std::string* error) { + // Normalize underscores to hyphens because that's what our locale files use. + std::replace(locale_name->begin(), locale_name->end(), '_', '-'); + if (chrome_locales.find(*locale_name) == chrome_locales.end()) { + // Fail if there is an extension locale that's not in the Chrome list. + *error = StringPrintf("Supplied locale %s is not supported.", + locale_name->c_str()); + return false; + } + // Check if messages file is actually present (but don't check content). + if (file_util::PathExists( + locale_folder.AppendASCII(Extension::kMessagesFilename))) { + extension->AddSupportedLocale(*locale_name); + } else { + *error = StringPrintf("Catalog file is missing for locale %s.", + locale_name->c_str()); + return false; + } + + return true; +} + +bool AddValidLocales(const FilePath& locale_path, + Extension* extension, + std::string* error) { + // Get available chrome locales as a set. + const std::vector<std::string>& available_locales = + l10n_util::GetAvailableLocales(); + static std::set<std::string> chrome_locales(available_locales.begin(), + available_locales.end()); + // Enumerate all supplied locales in the extension. + file_util::FileEnumerator locales(locale_path, + false, + file_util::FileEnumerator::DIRECTORIES); + FilePath locale_folder; + while (!(locale_folder = locales.Next()).empty()) { + std::string locale_name = + WideToASCII(locale_folder.BaseName().ToWStringHack()); + if (!AddLocale(chrome_locales, locale_folder, + extension, &locale_name, error)) { + return false; + } + } + + if (extension->supported_locales().empty()) { + *error = extension_manifest_errors::kLocalesNoValidLocaleNamesListed; + return false; + } + + return true; +} + +} // namespace extension_l10n_util diff --git a/chrome/browser/extensions/extension_l10n_util.h b/chrome/browser/extensions/extension_l10n_util.h new file mode 100644 index 0000000..d964566 --- /dev/null +++ b/chrome/browser/extensions/extension_l10n_util.h @@ -0,0 +1,46 @@ +// Copyright (c) 2009 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. +// +// This file declares extension specific l10n utils. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_L10N_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_L10N_UTIL_H_ + +#include <set> +#include <string> + +class DictionaryValue; +class Extension; +class FilePath; + +namespace extension_l10n_util { + +// Returns true if default_locale was set to valid locale +// (supported by the extension). +bool ValidateDefaultLocale(const Extension* extension); + +// Adds locale_name to the extension if it's in chrome_locales, and +// if messages file is present (we don't check content of messages file here). +// Returns false if locale_name was not found in chrome_locales, and sets +// error with locale_name. +bool AddLocale(const std::set<std::string>& chrome_locales, + const FilePath& locale_folder, + Extension* extension, + std::string* locale_name, + std::string* error); + +// Adds valid locales to the extension. +// 1. Do nothing if _locales directory is missing (not an error). +// 2. Get list of Chrome locales. +// 3. Enumerate all subdirectories of _locales directory. +// 4. Intersect both lists, and add intersection to the extension. +// Returns false if any of supplied locales don't match chrome list of locales. +// Fills out error with offending locale name. +bool AddValidLocales(const FilePath& locale_path, + Extension* extension, + std::string* error); + +} // namespace extension_l10n_util + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_L10N_UTIL_H_ diff --git a/chrome/browser/extensions/extension_l10n_util_unittest.cc b/chrome/browser/extensions/extension_l10n_util_unittest.cc new file mode 100644 index 0000000..04f1ac0 --- /dev/null +++ b/chrome/browser/extensions/extension_l10n_util_unittest.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2009 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/browser/extensions/extension_l10n_util.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/scoped_ptr.h" +#include "base/scoped_temp_dir.h" +#include "base/values.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace keys = extension_manifest_keys; + +namespace { + + Extension* CreateMinimalExtension(const std::string& default_locale) { +#if defined(OS_WIN) + FilePath path(FILE_PATH_LITERAL("C:\\foo")); +#elif defined(OS_POSIX) + FilePath path(FILE_PATH_LITERAL("/foo")); +#endif + Extension* extension = new Extension(path); + std::string error; + DictionaryValue input_value; + + // Test minimal extension + input_value.SetString(keys::kVersion, "1.0.0.0"); + input_value.SetString(keys::kName, "my extension"); + if (!default_locale.empty()) { + input_value.SetString(keys::kDefaultLocale, default_locale); + } + EXPECT_TRUE(extension->InitFromValue(input_value, false, &error)); + + return extension; +} + +TEST(ExtensionL10nUtil, AddValidLocalesEmptyLocaleFolder) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + + FilePath src_path = temp.path().AppendASCII(Extension::kLocaleFolder); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + scoped_ptr<Extension> extension(CreateMinimalExtension("")); + + std::string error; + EXPECT_FALSE(extension_l10n_util::AddValidLocales(src_path, + extension.get(), + &error)); + + EXPECT_TRUE(extension->supported_locales().empty()); +} + +TEST(ExtensionL10nUtil, AddValidLocalesWithValidLocaleNoMessagesFile) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + + FilePath src_path = temp.path().AppendASCII(Extension::kLocaleFolder); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + ASSERT_TRUE(file_util::CreateDirectory(src_path.AppendASCII("sr"))); + + scoped_ptr<Extension> extension(CreateMinimalExtension("")); + + std::string error; + EXPECT_FALSE(extension_l10n_util::AddValidLocales(src_path, + extension.get(), + &error)); + + EXPECT_TRUE(extension->supported_locales().empty()); +} + +TEST(ExtensionL10nUtil, AddValidLocalesWithValidLocalesAndMessagesFile) { + ScopedTempDir temp; + ASSERT_TRUE(temp.CreateUniqueTempDir()); + + FilePath src_path = temp.path().AppendASCII(Extension::kLocaleFolder); + ASSERT_TRUE(file_util::CreateDirectory(src_path)); + + FilePath locale_1 = src_path.AppendASCII("sr"); + ASSERT_TRUE(file_util::CreateDirectory(locale_1)); + + std::string data = "foobar"; + ASSERT_TRUE( + file_util::WriteFile(locale_1.AppendASCII(Extension::kMessagesFilename), + data.c_str(), data.length())); + + FilePath locale_2 = src_path.AppendASCII("en_US"); + ASSERT_TRUE(file_util::CreateDirectory(locale_2)); + + ASSERT_TRUE( + file_util::WriteFile(locale_2.AppendASCII(Extension::kMessagesFilename), + data.c_str(), data.length())); + + scoped_ptr<Extension> extension(CreateMinimalExtension("")); + + std::string error; + EXPECT_TRUE(extension_l10n_util::AddValidLocales(src_path, + extension.get(), + &error)); + + EXPECT_EQ(static_cast<unsigned int>(2), + extension->supported_locales().size()); +} + +TEST(ExtensionL10nUtil, SetDefaultLocaleGoodDefaultLocaleInManifest) { + scoped_ptr<Extension> extension(CreateMinimalExtension("sr")); + extension->AddSupportedLocale("sr"); + extension->AddSupportedLocale("en-US"); + + EXPECT_TRUE(extension_l10n_util::ValidateDefaultLocale(extension.get())); + EXPECT_EQ("sr", extension->default_locale()); +} + +TEST(ExtensionL10nUtil, SetDefaultLocaleNoDefaultLocaleInManifest) { + scoped_ptr<Extension> extension(CreateMinimalExtension("")); + extension->AddSupportedLocale("sr"); + extension->AddSupportedLocale("en-US"); + + EXPECT_FALSE(extension_l10n_util::ValidateDefaultLocale(extension.get())); +} + +TEST(ExtensionL10nUtil, SetDefaultLocaleWrongDefaultLocaleInManifest) { + scoped_ptr<Extension> extension(CreateMinimalExtension("ko")); + extension->AddSupportedLocale("sr"); + extension->AddSupportedLocale("en-US"); + + EXPECT_FALSE(extension_l10n_util::ValidateDefaultLocale(extension.get())); +} + +} // namespace |