summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-11-25 19:40:10 +0000
committerBen Murdoch <benm@google.com>2010-12-03 13:52:53 +0000
commit4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7 (patch)
tree938665d93a11fe7a6d0124e3c1e020d1f9d3f947 /base
parent7c627d87728a355737862918d144f98f69406954 (diff)
downloadexternal_chromium-4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7.zip
external_chromium-4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7.tar.gz
external_chromium-4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7.tar.bz2
Merge Chromium at r66597: Initial merge by git.
Change-Id: I9639f8a997f90ec219573aa22a49f5dbde78cc7b
Diffstat (limited to 'base')
-rw-r--r--base/base.gyp3
-rw-r--r--base/base.gypi17
-rw-r--r--base/base_paths_mac.mm5
-rw-r--r--base/command_line.cc21
-rw-r--r--base/command_line_unittest.cc27
-rw-r--r--base/crypto/encryptor.h7
-rw-r--r--base/crypto/encryptor_nss.cc3
-rw-r--r--base/crypto/encryptor_openssl.cc108
-rw-r--r--base/crypto/encryptor_unittest.cc122
-rw-r--r--base/crypto/encryptor_win.cc2
-rw-r--r--base/crypto/rsa_private_key.h11
-rw-r--r--base/crypto/rsa_private_key_openssl.cc111
-rw-r--r--base/crypto/symmetric_key.h18
-rw-r--r--base/crypto/symmetric_key_openssl.cc59
-rw-r--r--base/crypto/symmetric_key_unittest.cc149
-rw-r--r--base/file_path.cc2
-rw-r--r--base/file_util_mac.mm2
-rw-r--r--base/file_util_posix.cc4
-rw-r--r--base/i18n/rtl.cc16
-rw-r--r--base/i18n/rtl.h7
-rw-r--r--base/logging.cc11
-rw-r--r--base/mac_util.h1
-rw-r--r--base/mac_util.mm25
-rw-r--r--base/message_loop.cc12
-rw-r--r--base/message_pump_glib_x.cc143
-rw-r--r--base/message_pump_glib_x.h22
-rw-r--r--base/native_library.h4
-rw-r--r--base/native_library_linux.cc4
-rw-r--r--base/native_library_mac.mm3
-rw-r--r--base/native_library_win.cc4
-rw-r--r--base/openssl_util.cc88
-rw-r--r--base/openssl_util.h33
-rw-r--r--base/path_service.cc8
-rw-r--r--base/path_service.h5
-rw-r--r--base/pickle.cc16
-rw-r--r--base/pickle.h4
-rw-r--r--base/pickle_unittest.cc33
-rw-r--r--base/platform_thread_posix.cc8
-rw-r--r--base/process_util.h8
-rw-r--r--base/safe_strerror_posix.cc9
-rw-r--r--base/scoped_native_library.cc44
-rw-r--r--base/scoped_native_library.h36
-rw-r--r--base/scoped_ptr.h4
-rw-r--r--base/scoped_vector.h1
-rw-r--r--base/shared_memory.h8
-rw-r--r--base/shared_memory_win.cc9
-rw-r--r--base/singleton.h8
-rw-r--r--base/string16_unittest.cc2
-rw-r--r--base/string_split.cc57
-rw-r--r--base/string_split.h15
-rw-r--r--base/string_split_unittest.cc32
-rw-r--r--base/string_util.cc57
-rw-r--r--base/string_util.h30
-rw-r--r--base/string_util_unittest.cc32
-rw-r--r--base/stringize_macros.h57
-rw-r--r--base/stringize_macros_unittest.cc59
-rw-r--r--base/stringprintf.h2
-rw-r--r--base/thread_local.h15
-rw-r--r--base/thread_local_posix.cc4
-rw-r--r--base/thread_local_win.cc4
-rw-r--r--base/time_mac.cc4
-rw-r--r--base/time_unittest.cc13
-rw-r--r--base/tuple.h7
-rw-r--r--base/version.cc4
-rw-r--r--base/win/i18n.cc169
-rw-r--r--base/win/i18n.h32
-rw-r--r--base/win/i18n_unittest.cc42
67 files changed, 1538 insertions, 344 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 6ae1a95..f68359a 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -129,11 +129,13 @@
'simple_thread_unittest.cc',
'singleton_unittest.cc',
'stack_container_unittest.cc',
+ 'string16_unittest.cc',
'string_number_conversions_unittest.cc',
'string_piece_unittest.cc',
'string_split_unittest.cc',
'string_tokenizer_unittest.cc',
'string_util_unittest.cc',
+ 'stringize_macros_unittest.cc',
'stringprintf_unittest.cc',
'sys_info_unittest.cc',
'sys_string_conversions_mac_unittest.mm',
@@ -163,6 +165,7 @@
'win/event_trace_consumer_unittest.cc',
'win/event_trace_controller_unittest.cc',
'win/event_trace_provider_unittest.cc',
+ 'win/i18n_unittest.cc',
'win/pe_image_unittest.cc',
'win/registry_unittest.cc',
'win/scoped_bstr_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 78c97aa..8b876bd 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -121,6 +121,7 @@
'mac_util.h',
'mac_util.mm',
'mac/scoped_aedesc.h',
+ 'mac/scoped_cftyperef.h',
'mac/scoped_nsautorelease_pool.h',
'mac/scoped_nsautorelease_pool.mm',
'mach_ipc_mac.h',
@@ -142,7 +143,7 @@
'metrics/histogram.cc',
'metrics/histogram.h',
'metrics/nacl_histogram.cc',
- 'metrics/nacl_histogram.h',
+ 'metrics/nacl_histogram.h',
'metrics/stats_counters.cc',
'metrics/stats_counters.h',
'metrics/stats_table.cc',
@@ -196,8 +197,9 @@
'safe_strerror_posix.cc',
'safe_strerror_posix.h',
'scoped_callback_factory.h',
- 'mac/scoped_cftyperef.h',
'scoped_handle.h',
+ 'scoped_native_library.cc',
+ 'scoped_native_library.h',
'scoped_nsobject.h',
'scoped_open_process.h',
'scoped_ptr.h',
@@ -226,6 +228,7 @@
'string_util.cc',
'string_util.h',
'string_util_win.h',
+ 'stringize_macros.h',
'stringprintf.cc',
'stringprintf.h',
'sys_info.h',
@@ -278,6 +281,8 @@
'utf_string_conversions.h',
'values.cc',
'values.h',
+ 'version.cc',
+ 'version.h',
'vlog.cc',
'vlog.h',
'waitable_event.h',
@@ -290,6 +295,8 @@
'watchdog.h',
'weak_ptr.cc',
'weak_ptr.h',
+ 'win/i18n.cc',
+ 'win/i18n.h',
'win/pe_image.cc',
'win/event_trace_consumer.h',
'win/event_trace_controller.cc',
@@ -474,6 +481,8 @@
'nsimage_cache_mac.mm',
'nss_util.cc',
'nss_util.h',
+ 'openssl_util.cc',
+ 'openssl_util.h',
'setproctitle_linux.c',
'setproctitle_linux.h',
'sha2.cc',
@@ -486,8 +495,6 @@
'sync_socket_posix.cc',
'time_mac.cc',
'time_posix.cc',
- 'version.cc',
- 'version.h',
],
'conditions': [
[ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
@@ -563,6 +570,8 @@
'crypto/signature_verifier_openssl.cc',
'crypto/symmetric_key_openssl.cc',
'hmac_openssl.cc',
+ 'openssl_util.cc',
+ 'openssl_util.h',
'sha2_openssl.cc',
],
},],
diff --git a/base/base_paths_mac.mm b/base/base_paths_mac.mm
index 793bece..b9225d8 100644
--- a/base/base_paths_mac.mm
+++ b/base/base_paths_mac.mm
@@ -4,7 +4,7 @@
#include "base/base_paths_mac.h"
-#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
#include <mach-o/dyld.h>
#include "base/compiler_specific.h"
@@ -52,7 +52,8 @@ bool PathProviderMac(int key, FilePath* result) {
case base::DIR_APP_DATA:
return mac_util::GetUserDirectory(NSApplicationSupportDirectory, result);
case base::DIR_SOURCE_ROOT: {
- if (GetNSExecutablePath(result)) {
+ // Go through PathService to catch overrides.
+ if (PathService::Get(base::FILE_EXE, result)) {
// Start with the executable's directory.
*result = result->DirName();
if (mac_util::AmIBundled()) {
diff --git a/base/command_line.cc b/base/command_line.cc
index 2e6021d..b335e7c 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -224,7 +224,7 @@ void CommandLine::Init(int argc, const char* const* argv) {
#endif
}
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL)
// static
void CommandLine::SetProcTitle() {
// Build a single string which consists of all the arguments separated
@@ -431,7 +431,14 @@ void CommandLine::AppendArguments(const CommandLine& other,
// Verify include_program is used correctly.
// Logic could be shorter but this is clearer.
DCHECK_EQ(include_program, !other.GetProgram().empty());
- command_line_string_ += L" " + other.command_line_string_;
+ if (include_program)
+ program_ = other.program_;
+
+ if (!command_line_string_.empty())
+ command_line_string_ += L' ';
+
+ command_line_string_ += other.command_line_string_;
+
std::map<std::string, StringType>::const_iterator i;
for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
switches_[i->first] = i->second;
@@ -482,9 +489,15 @@ void CommandLine::AppendArguments(const CommandLine& other,
// Verify include_program is used correctly.
// Logic could be shorter but this is clearer.
DCHECK_EQ(include_program, !other.GetProgram().empty());
- size_t first_arg = include_program ? 0 : 1;
- for (size_t i = first_arg; i < other.argv_.size(); ++i)
+
+ if (include_program)
+ argv_[0] = other.argv_[0];
+
+ // Skip the first arg when copying since it's the program but push all
+ // arguments to our arg vector.
+ for (size_t i = 1; i < other.argv_.size(); ++i)
argv_.push_back(other.argv_[i]);
+
std::map<std::string, StringType>::const_iterator i;
for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
switches_[i->first] = i->second;
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc
index 9184aa3..5c525ae 100644
--- a/base/command_line_unittest.cc
+++ b/base/command_line_unittest.cc
@@ -5,8 +5,8 @@
#include <string>
#include <vector>
-#include "base/command_line.h"
#include "base/basictypes.h"
+#include "base/command_line.h"
#include "base/file_path.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -154,3 +154,28 @@ TEST(CommandLineTest, AppendSwitches) {
cl.command_line_string());
#endif
}
+
+// Tests that when AppendArguments is called that the program is set correctly
+// on the target CommandLine object and the switches from the source
+// CommandLine are added to the target.
+TEST(CommandLineTest, AppendArguments) {
+ CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program")));
+ cl1.AppendSwitch("switch1");
+ cl1.AppendSwitchASCII("switch2", "foo");
+
+ CommandLine cl2(CommandLine::NO_PROGRAM);
+ cl2.AppendArguments(cl1, true);
+ EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value());
+ EXPECT_EQ(cl1.command_line_string(), cl2.command_line_string());
+
+ CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1")));
+ c1.AppendSwitch("switch1");
+ CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2")));
+ c2.AppendSwitch("switch2");
+
+ c1.AppendArguments(c2, true);
+ EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value());
+ EXPECT_TRUE(c1.HasSwitch("switch1"));
+ EXPECT_TRUE(c1.HasSwitch("switch2"));
+}
+
diff --git a/base/crypto/encryptor.h b/base/crypto/encryptor.h
index f1d6f28..7718240 100644
--- a/base/crypto/encryptor.h
+++ b/base/crypto/encryptor.h
@@ -44,7 +44,12 @@ class Encryptor {
SymmetricKey* key_;
Mode mode_;
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+ bool Crypt(bool encrypt, // Pass true to encrypt, false to decrypt.
+ const std::string& input,
+ std::string* output);
+ std::string iv_;
+#elif defined(USE_NSS)
ScopedPK11Slot slot_;
ScopedSECItem param_;
#elif defined(OS_MACOSX)
diff --git a/base/crypto/encryptor_nss.cc b/base/crypto/encryptor_nss.cc
index ef77d9d..737e8f2 100644
--- a/base/crypto/encryptor_nss.cc
+++ b/base/crypto/encryptor_nss.cc
@@ -48,9 +48,6 @@ bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) {
}
bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
- if (plaintext.size() == 0)
- return false;
-
ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD,
CKA_ENCRYPT,
key_->key(),
diff --git a/base/crypto/encryptor_openssl.cc b/base/crypto/encryptor_openssl.cc
index 71a84be..44ae932 100644
--- a/base/crypto/encryptor_openssl.cc
+++ b/base/crypto/encryptor_openssl.cc
@@ -4,29 +4,123 @@
#include "base/crypto/encryptor.h"
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+
+#include "base/crypto/symmetric_key.h"
#include "base/logging.h"
+#include "base/openssl_util.h"
+#include "base/string_util.h"
namespace base {
-Encryptor::Encryptor() {
+namespace {
+
+const EVP_CIPHER* GetCipherForKey(SymmetricKey* key) {
+ switch (key->key().length()) {
+ case 16: return EVP_aes_128_cbc();
+ case 24: return EVP_aes_192_cbc();
+ case 32: return EVP_aes_256_cbc();
+ default: return NULL;
+ }
+}
+
+// On destruction this class will cleanup the ctx, and also clear the OpenSSL
+// ERR stack as a convenience.
+class ScopedCipherCTX {
+ public:
+ explicit ScopedCipherCTX() {
+ EVP_CIPHER_CTX_init(&ctx_);
+ }
+ ~ScopedCipherCTX() {
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ ClearOpenSSLERRStack();
+ }
+ EVP_CIPHER_CTX* get() { return &ctx_; }
+
+ private:
+ EVP_CIPHER_CTX ctx_;
+};
+
+} // namespace
+
+Encryptor::Encryptor()
+ : key_(NULL) {
}
Encryptor::~Encryptor() {
}
bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) {
- NOTIMPLEMENTED();
- return false;
+ DCHECK(key);
+ DCHECK_EQ(CBC, mode);
+
+ EnsureOpenSSLInit();
+ if (iv.size() != AES_BLOCK_SIZE)
+ return false;
+
+ if (GetCipherForKey(key) == NULL)
+ return false;
+
+ key_ = key;
+ mode_ = mode;
+ iv_ = iv;
+ return true;
}
bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
- NOTIMPLEMENTED();
- return false;
+ return Crypt(true, plaintext, ciphertext);
}
bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) {
- NOTIMPLEMENTED();
- return false;
+ return Crypt(false, ciphertext, plaintext);
+}
+
+bool Encryptor::Crypt(bool do_encrypt,
+ const std::string& input,
+ std::string* output) {
+ DCHECK(key_); // Must call Init() before En/De-crypt.
+ // Work on the result in a local variable, and then only transfer it to
+ // |output| on success to ensure no partial data is returned.
+ std::string result;
+ output->swap(result);
+
+ const EVP_CIPHER* cipher = GetCipherForKey(key_);
+ DCHECK(cipher); // Already handled in Init();
+
+ const std::string& key = key_->key();
+ DCHECK_EQ(EVP_CIPHER_iv_length(cipher), static_cast<int>(iv_.length()));
+ DCHECK_EQ(EVP_CIPHER_key_length(cipher), static_cast<int>(key.length()));
+
+ ScopedCipherCTX ctx;
+ if (!EVP_CipherInit_ex(ctx.get(), cipher, NULL,
+ reinterpret_cast<const uint8*>(key.data()),
+ reinterpret_cast<const uint8*>(iv_.data()),
+ do_encrypt))
+ return false;
+
+ // When encrypting, add another block size of space to allow for any padding.
+ const size_t output_size = input.size() + (do_encrypt ? iv_.size() : 0);
+ uint8* out_ptr = reinterpret_cast<uint8*>(WriteInto(&result,
+ output_size + 1));
+ int out_len;
+ if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len,
+ reinterpret_cast<const uint8*>(input.data()),
+ input.length()))
+ return false;
+
+ // Write out the final block plus padding (if any) to the end of the data
+ // just written.
+ int tail_len;
+ if (!EVP_CipherFinal_ex(ctx.get(), out_ptr + out_len, &tail_len))
+ return false;
+
+ out_len += tail_len;
+ DCHECK_LE(out_len, static_cast<int>(output_size));
+ result.resize(out_len);
+
+ output->swap(result);
+ return true;
}
} // namespace base
diff --git a/base/crypto/encryptor_unittest.cc b/base/crypto/encryptor_unittest.cc
index bc66e55..e8d055b 100644
--- a/base/crypto/encryptor_unittest.cc
+++ b/base/crypto/encryptor_unittest.cc
@@ -8,6 +8,7 @@
#include "base/crypto/symmetric_key.h"
#include "base/scoped_ptr.h"
+#include "base/string_number_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(EncryptorTest, EncryptDecrypt) {
@@ -108,3 +109,124 @@ TEST(EncryptorTest, EncryptAES256CBC) {
EXPECT_EQ(plaintext, decypted);
}
+
+// Expected output derived from the NSS implementation.
+TEST(EncryptorTest, EncryptAES128CBCRegression) {
+ std::string key = "128=SixteenBytes";
+ std::string iv = "Sweet Sixteen IV";
+ std::string plaintext = "Plain text with a g-clef U+1D11E \360\235\204\236";
+ std::string expected_ciphertext_hex =
+ "D4A67A0BA33C30F207344D81D1E944BBE65587C3D7D9939A"
+ "C070C62B9C15A3EA312EA4AD1BC7929F4D3C16B03AD5ADA8";
+
+ scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import(
+ base::SymmetricKey::AES, key));
+ ASSERT_TRUE(NULL != sym_key.get());
+
+ base::Encryptor encryptor;
+ // The IV must be exactly as long a the cipher block size.
+ EXPECT_EQ(16U, iv.size());
+ EXPECT_TRUE(encryptor.Init(sym_key.get(), base::Encryptor::CBC, iv));
+
+ std::string ciphertext;
+ EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
+ EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(),
+ ciphertext.size()));
+
+ std::string decypted;
+ EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted));
+ EXPECT_EQ(plaintext, decypted);
+}
+
+// Expected output derived from the NSS implementation.
+TEST(EncryptorTest, EncryptAES192CBCRegression) {
+ std::string key = "192bitsIsTwentyFourByte!";
+ std::string iv = "Sweet Sixteen IV";
+ std::string plaintext = "Small text";
+ std::string expected_ciphertext_hex = "78DE5D7C2714FC5C61346C5416F6C89A";
+
+ scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import(
+ base::SymmetricKey::AES, key));
+ ASSERT_TRUE(NULL != sym_key.get());
+
+ base::Encryptor encryptor;
+ // The IV must be exactly as long a the cipher block size.
+ EXPECT_EQ(16U, iv.size());
+ EXPECT_TRUE(encryptor.Init(sym_key.get(), base::Encryptor::CBC, iv));
+
+ std::string ciphertext;
+ EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
+ EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(),
+ ciphertext.size()));
+
+ std::string decypted;
+ EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted));
+ EXPECT_EQ(plaintext, decypted);
+}
+
+// Not all platforms allow import/generation of symmetric keys with an
+// unsupported size.
+#if !defined(OS_WIN) && !defined(USE_NSS)
+TEST(EncryptorTest, UnsupportedKeySize) {
+ std::string key = "7 = bad";
+ std::string iv = "Sweet Sixteen IV";
+ scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import(
+ base::SymmetricKey::AES, key));
+ ASSERT_TRUE(NULL != sym_key.get());
+
+ base::Encryptor encryptor;
+ // The IV must be exactly as long a the cipher block size.
+ EXPECT_EQ(16U, iv.size());
+ EXPECT_FALSE(encryptor.Init(sym_key.get(), base::Encryptor::CBC, iv));
+}
+#endif // unsupported platforms.
+
+TEST(EncryptorTest, UnsupportedIV) {
+ std::string key = "128=SixteenBytes";
+ std::string iv = "OnlyForteen :(";
+ scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import(
+ base::SymmetricKey::AES, key));
+ ASSERT_TRUE(NULL != sym_key.get());
+
+ base::Encryptor encryptor;
+ EXPECT_FALSE(encryptor.Init(sym_key.get(), base::Encryptor::CBC, iv));
+}
+
+TEST(EncryptorTest, EmptyEncrypt) {
+ std::string key = "128=SixteenBytes";
+ std::string iv = "Sweet Sixteen IV";
+ std::string plaintext;
+ std::string expected_ciphertext_hex = "8518B8878D34E7185E300D0FCC426396";
+
+ scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import(
+ base::SymmetricKey::AES, key));
+ ASSERT_TRUE(NULL != sym_key.get());
+
+ base::Encryptor encryptor;
+ // The IV must be exactly as long a the cipher block size.
+ EXPECT_EQ(16U, iv.size());
+ EXPECT_TRUE(encryptor.Init(sym_key.get(), base::Encryptor::CBC, iv));
+
+ std::string ciphertext;
+ EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
+ EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(),
+ ciphertext.size()));
+}
+
+TEST(EncryptorTest, EmptyDecrypt) {
+ std::string key = "128=SixteenBytes";
+ std::string iv = "Sweet Sixteen IV";
+
+ scoped_ptr<base::SymmetricKey> sym_key(base::SymmetricKey::Import(
+ base::SymmetricKey::AES, key));
+ ASSERT_TRUE(NULL != sym_key.get());
+
+ base::Encryptor encryptor;
+ // The IV must be exactly as long a the cipher block size.
+ EXPECT_EQ(16U, iv.size());
+ EXPECT_TRUE(encryptor.Init(sym_key.get(), base::Encryptor::CBC, iv));
+
+ std::string decrypted;
+ EXPECT_FALSE(encryptor.Decrypt("", &decrypted));
+ EXPECT_EQ("", decrypted);
+}
diff --git a/base/crypto/encryptor_win.cc b/base/crypto/encryptor_win.cc
index fe1f5a8..4a137b3 100644
--- a/base/crypto/encryptor_win.cc
+++ b/base/crypto/encryptor_win.cc
@@ -93,6 +93,8 @@ bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) {
DWORD data_len = ciphertext.size();
+ if (data_len == 0)
+ return false;
std::vector<BYTE> tmp(data_len);
memcpy(&tmp[0], ciphertext.data(), data_len);
diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h
index 4492c3d..ea5daac 100644
--- a/base/crypto/rsa_private_key.h
+++ b/base/crypto/rsa_private_key.h
@@ -8,7 +8,10 @@
#include "build/build_config.h"
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+// Forward declaration for openssl/*.h
+typedef struct evp_pkey_st EVP_PKEY;
+#elif defined(USE_NSS)
// Forward declaration.
struct SECKEYPrivateKeyStr;
struct SECKEYPublicKeyStr;
@@ -216,7 +219,7 @@ class RSAPrivateKey {
// Exports the public key to an X509 SubjectPublicKeyInfo block.
bool ExportPublicKey(std::vector<uint8>* output);
-private:
+ private:
#if defined(USE_NSS)
FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FindFromPublicKey);
FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FailedFindFromPublicKey);
@@ -238,7 +241,9 @@ private:
static RSAPrivateKey* CreateFromPrivateKeyInfoWithParams(
const std::vector<uint8>& input, bool permanent, bool sensitive);
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+ EVP_PKEY* key_;
+#elif defined(USE_NSS)
SECKEYPrivateKeyStr* key_;
SECKEYPublicKeyStr* public_key_;
#elif defined(OS_WIN)
diff --git a/base/crypto/rsa_private_key_openssl.cc b/base/crypto/rsa_private_key_openssl.cc
index ec1d8b5..e14965f 100644
--- a/base/crypto/rsa_private_key_openssl.cc
+++ b/base/crypto/rsa_private_key_openssl.cc
@@ -4,35 +4,69 @@
#include "base/crypto/rsa_private_key.h"
+#include <openssl/evp.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rsa.h>
+
#include "base/logging.h"
+#include "base/openssl_util.h"
+#include "base/scoped_ptr.h"
+#include "base/stl_util-inl.h"
namespace base {
-// static
-RSAPrivateKey* RSAPrivateKey::CreateWithParams(uint16 num_bits,
- bool permanent,
- bool sensitive) {
- NOTIMPLEMENTED();
- return NULL;
+namespace {
+
+// Function pointer definition, for injecting the required key export function
+// into ExportKey, below. The supplied function should export EVP_PKEY into
+// the supplied BIO, returning 1 on success or 0 on failure.
+typedef int (ExportFunction)(BIO*, EVP_PKEY*);
+
+// Helper to export |key| into |output| via the specified ExportFunction.
+bool ExportKey(EVP_PKEY* key,
+ ExportFunction export_fn,
+ std::vector<uint8>* output) {
+ if (!key)
+ return false;
+
+ ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new(BIO_s_mem()));
+
+ int res = export_fn(bio.get(), key);
+ ClearOpenSSLERRStack();
+ if (!res)
+ return false;
+
+ char* data = NULL;
+ long len = BIO_get_mem_data(bio.get(), &data);
+ if (!data || len < 0)
+ return false;
+
+ STLAssignToVector(output, reinterpret_cast<const uint8*>(data), len);
+ return true;
}
+} // namespace
+
// static
RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) {
- return CreateWithParams(num_bits,
- false /* not permanent */,
- false /* not sensitive */);
-}
+ EnsureOpenSSLInit();
-// static
-RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) {
- return CreateWithParams(num_bits,
- true /* permanent */,
- true /* sensitive */);
+ ScopedOpenSSL<RSA, RSA_free> rsa_key(RSA_generate_key(num_bits, 65537L,
+ NULL, NULL));
+ ClearOpenSSLERRStack();
+ if (!rsa_key.get())
+ return NULL;
+
+ scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey);
+ result->key_ = EVP_PKEY_new();
+ if (!result->key_ || !EVP_PKEY_set1_RSA(result->key_, rsa_key.get()))
+ return NULL;
+
+ return result.release();
}
// static
-RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams(
- const std::vector<uint8>& input, bool permanent, bool sensitive) {
+RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) {
NOTIMPLEMENTED();
return NULL;
}
@@ -40,17 +74,37 @@ RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams(
// static
RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo(
const std::vector<uint8>& input) {
- return CreateFromPrivateKeyInfoWithParams(input,
- false /* not permanent */,
- false /* not sensitive */);
+ EnsureOpenSSLInit();
+
+ // BIO_new_mem_buf is not const aware, but it does not modify the buffer.
+ char* data = reinterpret_cast<char*>(const_cast<uint8*>(input.data()));
+ ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new_mem_buf(data, input.size()));
+ if (!bio.get())
+ return NULL;
+
+ // Importing is a little more involved than exporting, as we must first
+ // PKCS#8 decode the input, and then import the EVP_PKEY from Private Key
+ // Info structure returned.
+ ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> p8inf(
+ d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
+ ClearOpenSSLERRStack();
+ if (!p8inf.get())
+ return NULL;
+
+ scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey);
+ result->key_ = EVP_PKCS82PKEY(p8inf.get());
+ ClearOpenSSLERRStack();
+ if (!result->key_)
+ return NULL;
+
+ return result.release();
}
// static
RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo(
const std::vector<uint8>& input) {
- return CreateFromPrivateKeyInfoWithParams(input,
- true /* permanent */,
- true /* seneitive */);
+ NOTIMPLEMENTED();
+ return NULL;
}
// static
@@ -60,20 +114,21 @@ RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo(
return NULL;
}
-RSAPrivateKey::RSAPrivateKey() {
+RSAPrivateKey::RSAPrivateKey()
+ : key_(NULL) {
}
RSAPrivateKey::~RSAPrivateKey() {
+ if (key_)
+ EVP_PKEY_free(key_);
}
bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) {
- NOTIMPLEMENTED();
- return false;
+ return ExportKey(key_, i2d_PKCS8PrivateKeyInfo_bio, output);
}
bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) {
- NOTIMPLEMENTED();
- return false;
+ return ExportKey(key_, i2d_PUBKEY_bio, output);
}
} // namespace base
diff --git a/base/crypto/symmetric_key.h b/base/crypto/symmetric_key.h
index 3f2be76..6ad0646 100644
--- a/base/crypto/symmetric_key.h
+++ b/base/crypto/symmetric_key.h
@@ -24,6 +24,8 @@ namespace base {
// scoped_ptr.
class SymmetricKey {
public:
+ // Defines the algorithm that a key will be used with. See also
+ // classs Encrptor.
enum Algorithm {
AES,
HMAC_SHA1,
@@ -31,14 +33,16 @@ class SymmetricKey {
virtual ~SymmetricKey();
- // Generates a random key suitable to be used with |cipher| and of
+ // Generates a random key suitable to be used with |algorithm| and of
// |key_size_in_bits| bits.
// The caller is responsible for deleting the returned SymmetricKey.
static SymmetricKey* GenerateRandomKey(Algorithm algorithm,
size_t key_size_in_bits);
- // Derives a key from the supplied password and salt using PBKDF2. The caller
- // is responsible for deleting the returned SymmetricKey.
+ // Derives a key from the supplied password and salt using PBKDF2, suitable
+ // for use with specified |algorithm|. Note |algorithm| is not the algorithm
+ // used to derive the key from the password. The caller is responsible for
+ // deleting the returned SymmetricKey.
static SymmetricKey* DeriveKeyFromPassword(Algorithm algorithm,
const std::string& password,
const std::string& salt,
@@ -51,7 +55,9 @@ class SymmetricKey {
// SymmetricKey.
static SymmetricKey* Import(Algorithm algorithm, const std::string& raw_key);
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+ const std::string& key() { return key_; }
+#elif defined(USE_NSS)
PK11SymKey* key() const { return key_.get(); }
#elif defined(OS_MACOSX)
CSSM_DATA cssm_data() const;
@@ -66,8 +72,8 @@ class SymmetricKey {
private:
#if defined(USE_OPENSSL)
- // TODO(joth): Add a constructor that accepts OpenSSL symmetric key data, and
- // the appropriate data members to store it in.
+ SymmetricKey() {}
+ std::string key_;
#elif defined(USE_NSS)
explicit SymmetricKey(PK11SymKey* key);
ScopedPK11SymKey key_;
diff --git a/base/crypto/symmetric_key_openssl.cc b/base/crypto/symmetric_key_openssl.cc
index 591252d..9f0ad38 100644
--- a/base/crypto/symmetric_key_openssl.cc
+++ b/base/crypto/symmetric_key_openssl.cc
@@ -4,18 +4,44 @@
#include "base/crypto/symmetric_key.h"
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <algorithm>
+
#include "base/logging.h"
+#include "base/openssl_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
namespace base {
SymmetricKey::~SymmetricKey() {
+ std::fill(key_.begin(), key_.end(), '\0'); // Zero out the confidential key.
}
// static
SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
size_t key_size_in_bits) {
- NOTIMPLEMENTED();
- return NULL;
+ DCHECK_EQ(AES, algorithm);
+ int key_size_in_bytes = key_size_in_bits / 8;
+ DCHECK_EQ(static_cast<int>(key_size_in_bits), key_size_in_bytes * 8);
+
+ if (key_size_in_bits == 0)
+ return NULL;
+
+ EnsureOpenSSLInit();
+ scoped_ptr<SymmetricKey> key(new SymmetricKey);
+ uint8* key_data =
+ reinterpret_cast<uint8*>(WriteInto(&key->key_, key_size_in_bytes + 1));
+
+ int res = RAND_bytes(key_data, key_size_in_bytes);
+ if (res != 1) {
+ DLOG(ERROR) << "RAND_bytes failed. res = " << res;
+ ClearOpenSSLERRStack();
+ return NULL;
+ }
+ return key.release();
}
// static
@@ -24,20 +50,37 @@ SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
const std::string& salt,
size_t iterations,
size_t key_size_in_bits) {
- NOTIMPLEMENTED();
- return NULL;
+ DCHECK(algorithm == AES || algorithm == HMAC_SHA1);
+ int key_size_in_bytes = key_size_in_bits / 8;
+ DCHECK_EQ(static_cast<int>(key_size_in_bits), key_size_in_bytes * 8);
+
+ EnsureOpenSSLInit();
+ scoped_ptr<SymmetricKey> key(new SymmetricKey);
+ uint8* key_data =
+ reinterpret_cast<uint8*>(WriteInto(&key->key_, key_size_in_bytes + 1));
+ int res = PKCS5_PBKDF2_HMAC_SHA1(password.data(), password.length(),
+ reinterpret_cast<const uint8*>(salt.data()),
+ salt.length(), iterations,
+ key_size_in_bytes, key_data);
+ if (res != 1) {
+ DLOG(ERROR) << "HMAC SHA1 failed. res = " << res;
+ ClearOpenSSLERRStack();
+ return NULL;
+ }
+ return key.release();
}
// static
SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
const std::string& raw_key) {
- NOTIMPLEMENTED();
- return NULL;
+ scoped_ptr<SymmetricKey> key(new SymmetricKey);
+ key->key_ = raw_key;
+ return key.release();
}
bool SymmetricKey::GetRawKey(std::string* raw_key) {
- NOTIMPLEMENTED();
- return false;
+ *raw_key = key_;
+ return true;
}
} // namespace base
diff --git a/base/crypto/symmetric_key_unittest.cc b/base/crypto/symmetric_key_unittest.cc
index 664bcb5..a9b0b9e 100644
--- a/base/crypto/symmetric_key_unittest.cc
+++ b/base/crypto/symmetric_key_unittest.cc
@@ -7,6 +7,7 @@
#include <string>
#include "base/scoped_ptr.h"
+#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -66,156 +67,160 @@ TEST(SymmetricKeyTest, ImportDerivedKey) {
}
struct PBKDF2TestVector {
+ base::SymmetricKey::Algorithm algorithm;
const char* password;
const char* salt;
unsigned int rounds;
unsigned int key_size_in_bits;
- const uint8 expected[21]; // string literals need 1 extra NUL byte
+ const char* expected; // ASCII encoded hex bytes
};
-static const PBKDF2TestVector test_vectors[] = {
+class SymmetricKeyDeriveKeyFromPasswordTest
+ : public testing::TestWithParam<PBKDF2TestVector> {
+};
+
+TEST_P(SymmetricKeyDeriveKeyFromPasswordTest, DeriveKeyFromPassword) {
+ PBKDF2TestVector test_data(GetParam());
+#if defined(OS_MACOSX)
+ // The OS X crypto libraries have minimum salt and iteration requirements
+ // so some of the tests below will cause them to barf. Skip these.
+ if (strlen(test_data.salt) < 8 || test_data.rounds < 1000) {
+ VLOG(1) << "Skipped test vector for " << test_data.expected;
+ return;
+ }
+#endif // OS_MACOSX
+
+ scoped_ptr<base::SymmetricKey> key(
+ base::SymmetricKey::DeriveKeyFromPassword(
+ test_data.algorithm,
+ test_data.password, test_data.salt,
+ test_data.rounds, test_data.key_size_in_bits));
+ ASSERT_TRUE(NULL != key.get());
+
+ std::string raw_key;
+ key->GetRawKey(&raw_key);
+ EXPECT_EQ(test_data.key_size_in_bits / 8, raw_key.size());
+ EXPECT_EQ(test_data.expected,
+ StringToLowerASCII(base::HexEncode(raw_key.data(),
+ raw_key.size())));
+}
+
+static const PBKDF2TestVector kTestVectors[] = {
// These tests come from
// http://www.ietf.org/id/draft-josefsson-pbkdf2-test-vectors-00.txt
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"salt",
1,
160,
- "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9"
- "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6",
+ "0c60c80f961f0e71f3a9b524af6012062fe037a6",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"salt",
2,
160,
- "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e"
- "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57",
+ "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"salt",
4096,
160,
- "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad"
- "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1",
+ "4b007901b765489abead49d926f721d065a429c1",
},
// This test takes over 30s to run on the trybots.
#if 0
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"salt",
16777216,
160,
- "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94"
- "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84",
+ "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984",
},
#endif
// These tests come from RFC 3962, via BSD source code at
// http://www.openbsd.org/cgi-bin/cvsweb/src/sbin/bioctl/pbkdf2.c?rev=HEAD&content-type=text/plain
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"ATHENA.MIT.EDUraeburn",
1,
160,
- {
- 0xcd, 0xed, 0xb5, 0x28, 0x1b, 0xb2, 0xf8, 0x01,
- 0x56, 0x5a, 0x11, 0x22, 0xb2, 0x56, 0x35, 0x15,
- 0x0a, 0xd1, 0xf7, 0xa0
- },
+ "cdedb5281bb2f801565a1122b25635150ad1f7a0",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"ATHENA.MIT.EDUraeburn",
2,
160,
- {
- 0x01, 0xdb, 0xee, 0x7f, 0x4a, 0x9e, 0x24, 0x3e,
- 0x98, 0x8b, 0x62, 0xc7, 0x3c, 0xda, 0x93, 0x5d,
- 0xa0, 0x53, 0x78, 0xb9
- },
+ "01dbee7f4a9e243e988b62c73cda935da05378b9",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"ATHENA.MIT.EDUraeburn",
1200,
160,
- {
- 0x5c, 0x08, 0xeb, 0x61, 0xfd, 0xf7, 0x1e, 0x4e,
- 0x4e, 0xc3, 0xcf, 0x6b, 0xa1, 0xf5, 0x51, 0x2b,
- 0xa7, 0xe5, 0x2d, 0xdb
- },
+ "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddb",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"password",
"\0224VxxV4\022", /* 0x1234567878563412 */
5,
160,
- {
- 0xd1, 0xda, 0xa7, 0x86, 0x15, 0xf2, 0x87, 0xe6,
- 0xa1, 0xc8, 0xb1, 0x20, 0xd7, 0x06, 0x2a, 0x49,
- 0x3f, 0x98, 0xd2, 0x03
- },
+ "d1daa78615f287e6a1c8b120d7062a493f98d203",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"pass phrase equals block size",
1200,
160,
- {
- 0x13, 0x9c, 0x30, 0xc0, 0x96, 0x6b, 0xc3, 0x2b,
- 0xa5, 0x5f, 0xdb, 0xf2, 0x12, 0x53, 0x0a, 0xc9,
- 0xc5, 0xec, 0x59, 0xf1
- },
+ "139c30c0966bc32ba55fdbf212530ac9c5ec59f1",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"pass phrase exceeds block size",
1200,
160,
- {
- 0x9c, 0xca, 0xd6, 0xd4, 0x68, 0x77, 0x0c, 0xd5,
- 0x1b, 0x10, 0xe6, 0xa6, 0x87, 0x21, 0xbe, 0x61,
- 0x1a, 0x8b, 0x4d, 0x28
- },
+ "9ccad6d468770cd51b10e6a68721be611a8b4d28",
},
{
+ base::SymmetricKey::HMAC_SHA1,
"\360\235\204\236", /* g-clef (0xf09d849e) */
"EXAMPLE.COMpianist",
50,
160,
- {
- 0x6b, 0x9c, 0xf2, 0x6d, 0x45, 0x45, 0x5a, 0x43,
- 0xa5, 0xb8, 0xbb, 0x27, 0x6a, 0x40, 0x3b, 0x39,
- 0xe7, 0xfe, 0x37, 0xa0
- },
- }
+ "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0",
+ },
+
+ // Regression tests for AES keys, derived from the Linux NSS implementation.
+ {
+ base::SymmetricKey::AES,
+ "A test password",
+ "saltsalt",
+ 1,
+ 256,
+ "44899a7777f0e6e8b752f875f02044b8ac593de146de896f2e8a816e315a36de",
+ },
+ {
+ base::SymmetricKey::AES,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "pass phrase exceeds block size",
+ 20,
+ 256,
+ "e0739745dc28b8721ba402e05214d2ac1eab54cf72bee1fba388297a09eb493c",
+ },
};
-TEST(SymmetricKeyTest, DeriveKeyFromPassword) {
- for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_vectors); ++i) {
- SCOPED_TRACE(StringPrintf("Test[%u]", i));
-#if defined(OS_MACOSX)
- // The OS X crypto libraries have minimum salt and iteration requirements
- // so some of the above tests will cause them to barf. Skip these.
- if (strlen(test_vectors[i].salt) < 8 || test_vectors[i].rounds < 1000) {
- VLOG(1) << "Skipped test vector #" << i;
- continue;
- }
-#endif // OS_MACOSX
- scoped_ptr<base::SymmetricKey> key(
- base::SymmetricKey::DeriveKeyFromPassword(
- base::SymmetricKey::HMAC_SHA1,
- test_vectors[i].password, test_vectors[i].salt,
- test_vectors[i].rounds, test_vectors[i].key_size_in_bits));
- ASSERT_TRUE(NULL != key.get());
-
- std::string raw_key;
- key->GetRawKey(&raw_key);
- EXPECT_EQ(test_vectors[i].key_size_in_bits / 8, raw_key.size());
- EXPECT_EQ(0, memcmp(test_vectors[i].expected,
- raw_key.data(),
- raw_key.size()));
- }
-}
+INSTANTIATE_TEST_CASE_P(, SymmetricKeyDeriveKeyFromPasswordTest,
+ testing::ValuesIn(kTestVectors));
diff --git a/base/file_path.cc b/base/file_path.cc
index 5165828..eba9afe 100644
--- a/base/file_path.cc
+++ b/base/file_path.cc
@@ -7,7 +7,7 @@
#if defined(OS_WIN)
#include <windows.h>
#elif defined(OS_MACOSX)
-#include <CoreServices/CoreServices.h>
+#include <CoreFoundation/CoreFoundation.h>
#endif
#include "base/logging.h"
diff --git a/base/file_util_mac.mm b/base/file_util_mac.mm
index 43bf6e9..ca0284c 100644
--- a/base/file_util_mac.mm
+++ b/base/file_util_mac.mm
@@ -4,7 +4,7 @@
#include "base/file_util.h"
-#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
#include <copyfile.h>
#include "base/basictypes.h"
diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc
index 85123e9..f1caa29 100644
--- a/base/file_util_posix.cc
+++ b/base/file_util_posix.cc
@@ -132,7 +132,7 @@ int CountFilesCreatedAfter(const FilePath& path,
// which are older than |comparison_time| are considered newer
// (current implementation) 2. files newer than
// |comparison_time| are considered older.
- if (st.st_ctime >= comparison_time.ToTimeT())
+ if (static_cast<time_t>(st.st_ctime) >= comparison_time.ToTimeT())
++file_count;
}
closedir(dir);
@@ -762,7 +762,7 @@ void MemoryMappedFile::CloseHandles() {
bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
const base::Time& cutoff_time) {
- return find_info.stat.st_mtime >= cutoff_time.ToTimeT();
+ return static_cast<time_t>(find_info.stat.st_mtime) >= cutoff_time.ToTimeT();
}
bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc
index 9ff12d8..0881fb7 100644
--- a/base/i18n/rtl.cc
+++ b/base/i18n/rtl.cc
@@ -44,22 +44,6 @@ namespace i18n {
// Represents the locale-specific ICU text direction.
static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION;
-#if defined(OS_WIN)
-void GetLanguageAndRegionFromOS(std::string* lang, std::string* region) {
- // Later we may have to change this to be OS-dependent so that
- // it's not affected by ICU's default locale. It's all right
- // to do this way because SetICUDefaultLocale is internal
- // to this file and we know that it's not yet called when this function
- // is called.
- const icu::Locale& locale = icu::Locale::getDefault();
- const char* language = locale.getLanguage();
- const char* country = locale.getCountry();
- DCHECK(language);
- *lang = language;
- *region = country;
-}
-#endif
-
// Convert the ICU default locale to a string.
std::string GetConfiguredLocale() {
return GetLocaleString(icu::Locale::getDefault());
diff --git a/base/i18n/rtl.h b/base/i18n/rtl.h
index 52b1a2b..ed0882f 100644
--- a/base/i18n/rtl.h
+++ b/base/i18n/rtl.h
@@ -6,6 +6,8 @@
#define BASE_I18N_RTL_H_
#pragma once
+#include <string>
+
#include "base/compiler_specific.h"
#include "base/string16.h"
#include "build/build_config.h"
@@ -29,11 +31,6 @@ enum TextDirection {
LEFT_TO_RIGHT,
};
-#if defined(OS_WIN)
-// Get language and region from the OS. Used by Chrome Frame.
-void GetLanguageAndRegionFromOS(std::string* lang, std::string* region);
-#endif
-
// Get the locale that the currently running process has been configured to use.
// The return value is of the form language[-country] (e.g., en-US) where the
// language is the 2 or 3 letter code from ISO-639.
diff --git a/base/logging.cc b/base/logging.cc
index d8681a9..9cc191d 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -19,7 +19,12 @@ typedef HANDLE MutexHandle;
#include <mach/mach_time.h>
#include <mach-o/dyld.h>
#elif defined(OS_POSIX)
+#if defined(OS_NACL)
+#include <sys/nacl_syscalls.h>
+#include <sys/time.h> // timespec doesn't seem to be in <time.h>
+#else
#include <sys/syscall.h>
+#endif
#include <time.h>
#endif
@@ -132,6 +137,8 @@ int32 CurrentThreadId() {
#elif defined(OS_FREEBSD)
// TODO(BSD): find a better thread ID
return reinterpret_cast<int64>(pthread_self());
+#elif defined(OS_NACL)
+ return pthread_self();
#endif
}
@@ -140,6 +147,10 @@ uint64 TickCount() {
return GetTickCount();
#elif defined(OS_MACOSX)
return mach_absolute_time();
+#elif defined(OS_NACL)
+ // NaCl sadly does not have _POSIX_TIMERS enabled in sys/features.h
+ // So we have to use clock() for now.
+ return clock();
#elif defined(OS_POSIX)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
diff --git a/base/mac_util.h b/base/mac_util.h
index 182fcc8..d31bf82 100644
--- a/base/mac_util.h
+++ b/base/mac_util.h
@@ -50,6 +50,7 @@ bool FSRefFromPath(const std::string& path, FSRef* ref);
// Returns true if the application is running from a bundle
bool AmIBundled();
+void SetOverrideAmIBundled(bool value);
// Returns true if this process is marked as a "Background only process".
bool IsBackgroundOnlyProcess();
diff --git a/base/mac_util.mm b/base/mac_util.mm
index 4e6b105..9610d37 100644
--- a/base/mac_util.mm
+++ b/base/mac_util.mm
@@ -145,8 +145,14 @@ bool FSRefFromPath(const std::string& path, FSRef* ref) {
return status == noErr;
}
+static bool g_override_am_i_bundled = false;
+static bool g_override_am_i_bundled_value = false;
+
// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
-bool AmIBundled() {
+static bool UncachedAmIBundled() {
+ if (g_override_am_i_bundled)
+ return g_override_am_i_bundled_value;
+
ProcessSerialNumber psn = {0, kCurrentProcess};
FSRef fsref;
@@ -167,6 +173,23 @@ bool AmIBundled() {
return info.nodeFlags & kFSNodeIsDirectoryMask;
}
+bool AmIBundled() {
+ // If the return value is not cached, this function will return different
+ // values depending on when it's called. This confuses some client code, see
+ // http://crbug.com/63183 .
+ static bool result = UncachedAmIBundled();
+ DCHECK_EQ(result, UncachedAmIBundled())
+ << "The return value of AmIBundled() changed. This will confuse tests. "
+ << "Call SetAmIBundled() override manually if your test binary "
+ << "delay-loads the framework.";
+ return result;
+}
+
+void SetOverrideAmIBundled(bool value) {
+ g_override_am_i_bundled = true;
+ g_override_am_i_bundled_value = value;
+}
+
bool IsBackgroundOnlyProcess() {
// This function really does want to examine NSBundle's idea of the main
// bundle dictionary, and not the overriden MainAppBundle. It needs to look
diff --git a/base/message_loop.cc b/base/message_loop.cc
index de0b271..1e472e0 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -163,7 +163,7 @@ MessageLoop::MessageLoop(Type type)
}
MessageLoop::~MessageLoop() {
- DCHECK(this == current());
+ DCHECK_EQ(this, current());
// Let interested parties have one last shot at accessing this.
FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
@@ -194,13 +194,13 @@ MessageLoop::~MessageLoop() {
void MessageLoop::AddDestructionObserver(
DestructionObserver* destruction_observer) {
- DCHECK(this == current());
+ DCHECK_EQ(this, current());
destruction_observers_.AddObserver(destruction_observer);
}
void MessageLoop::RemoveDestructionObserver(
DestructionObserver* destruction_observer) {
- DCHECK(this == current());
+ DCHECK_EQ(this, current());
destruction_observers_.RemoveObserver(destruction_observer);
}
@@ -254,7 +254,7 @@ __declspec(noinline) void MessageLoop::RunInternalInSEHFrame() {
//------------------------------------------------------------------------------
void MessageLoop::RunInternal() {
- DCHECK(this == current());
+ DCHECK_EQ(this, current());
#ifndef ANDROID
StartHistogrammer();
@@ -291,7 +291,7 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
//------------------------------------------------------------------------------
void MessageLoop::Quit() {
- DCHECK(current() == this);
+ DCHECK_EQ(this, current());
if (state_) {
state_->quit_received = true;
} else {
@@ -300,7 +300,7 @@ void MessageLoop::Quit() {
}
void MessageLoop::QuitNow() {
- DCHECK(current() == this);
+ DCHECK_EQ(this, current());
if (state_) {
pump_->Quit();
} else {
diff --git a/base/message_pump_glib_x.cc b/base/message_pump_glib_x.cc
index 675774e..78c1799 100644
--- a/base/message_pump_glib_x.cc
+++ b/base/message_pump_glib_x.cc
@@ -5,7 +5,11 @@
#include "base/message_pump_glib_x.h"
#include <gdk/gdkx.h>
+#if defined(HAVE_XINPUT2)
+#include <X11/extensions/XInput2.h>
+#else
#include <X11/Xlib.h>
+#endif
#include "base/message_pump_glib_x_dispatch.h"
@@ -17,17 +21,61 @@ gboolean PlaceholderDispatch(GSource* source,
return TRUE;
}
+#if defined(HAVE_XINPUT2)
+
+// Setup XInput2 select for the GtkWidget.
+gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams,
+ const GValue* pvalues, gpointer data) {
+ GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues));
+ GdkWindow* window = widget->window;
+ base::MessagePumpGlibX* msgpump = static_cast<base::MessagePumpGlibX*>(data);
+
+ DCHECK(window); // TODO(sad): Remove once determined if necessary.
+
+ // TODO(sad): Do we need to set a flag on |window| to make sure we don't
+ // select for the same GdkWindow multiple times? Does it matter?
+ msgpump->SetupXInput2ForXWindow(GDK_WINDOW_XID(window));
+
+ return true;
+}
+
+// We need to capture all the GDK windows that get created, and start
+// listening for XInput2 events. So we setup a callback to the 'realize'
+// signal for GTK+ widgets, so that whenever the signal triggers for any
+// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can
+// setup XInput2 events for the GdkWindow.
+void SetupGtkWidgetRealizeNotifier(base::MessagePumpGlibX* msgpump) {
+ guint signal_id;
+ gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);
+
+ g_signal_parse_name("realize", GTK_TYPE_WIDGET, &signal_id, NULL, FALSE);
+ g_signal_add_emission_hook(signal_id, 0, GtkWidgetRealizeCallback,
+ static_cast<gpointer>(msgpump), NULL);
+
+ g_type_class_unref(klass);
+}
+
+#endif // HAVE_XINPUT2
+
} // namespace
namespace base {
MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(),
+#if defined(HAVE_XINPUT2)
+ xiopcode_(-1),
+ masters_(),
+ slaves_(),
+#endif
gdksource_(NULL),
dispatching_event_(false),
capture_x_events_(0),
capture_gdk_events_(0) {
gdk_event_handler_set(&EventDispatcherX, this, NULL);
+#if defined(HAVE_XINPUT2)
+ InitializeXInput2();
+#endif
InitializeEventsToCapture();
}
@@ -40,7 +88,11 @@ bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) {
if (XPending(display)) {
XEvent xev;
XPeekEvent(display, &xev);
- if (capture_x_events_[xev.type]) {
+ if (capture_x_events_[xev.type]
+#if defined(HAVE_XINPUT2)
+ && (xev.type != GenericEvent || xev.xcookie.extension == xiopcode_)
+#endif
+ ) {
XNextEvent(display, &xev);
bool processed = static_cast<MessagePumpGlibXDispatcher*>
@@ -48,6 +100,13 @@ bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) {
if (!processed) {
DLOG(WARNING) << "Event (" << xev.type << ") not handled.";
+
+ // TODO(sad): It is necessary to put back the event so that the default
+ // GDK events handler can take care of it. Without this, it is
+ // impossible to use the omnibox at the moment. However, this will
+ // eventually be removed once the omnibox code is updated for touchui.
+ XPutBackEvent(display, &xev);
+ g_main_context_iteration(context, FALSE);
}
} else {
// TODO(sad): A couple of extra events can still sneak in during this.
@@ -94,8 +153,90 @@ void MessagePumpGlibX::InitializeEventsToCapture(void) {
capture_x_events_[MotionNotify] = true;
capture_gdk_events_[GDK_MOTION_NOTIFY] = true;
+
+#if defined(HAVE_XINPUT2)
+ capture_x_events_[GenericEvent] = true;
+#endif
+}
+
+#if defined(HAVE_XINPUT2)
+void MessagePumpGlibX::InitializeXInput2(void) {
+ GdkDisplay* display = gdk_display_get_default();
+ Display* xdisplay = GDK_DISPLAY_XDISPLAY(display);
+ int event, err;
+
+ if (!XQueryExtension(xdisplay, "XInputExtension", &xiopcode_, &event, &err)) {
+ DLOG(WARNING) << "X Input extension not available.";
+ xiopcode_ = -1;
+ return;
+ }
+
+ int major = 2, minor = 0;
+ if (XIQueryVersion(xdisplay, &major, &minor) == BadRequest) {
+ DLOG(WARNING) << "XInput2 not supported in the server.";
+ xiopcode_ = -1;
+ return;
+ }
+
+ SetupGtkWidgetRealizeNotifier(this);
+
+ // Instead of asking X for the list of devices all the time, let's maintain a
+ // list of slave (physical) and master (virtual) pointer devices.
+ int count = 0;
+ XIDeviceInfo* devices = XIQueryDevice(xdisplay, XIAllDevices, &count);
+ for (int i = 0; i < count; i++) {
+ XIDeviceInfo* devinfo = devices + i;
+ if (devinfo->use == XISlavePointer) {
+ slaves_.insert(devinfo->deviceid);
+ } else if (devinfo->use == XIMasterPointer) {
+ masters_.insert(devinfo->deviceid);
+ }
+ // We do not need to care about XIFloatingSlave, because the callback for
+ // XI_HierarchyChanged event will take care of it.
+ }
+ XIFreeDeviceInfo(devices);
+
+ // TODO(sad): Select on root for XI_HierarchyChanged so that slaves_ and
+ // masters_ can be kept up-to-date. This is a relatively rare event, so we can
+ // put it off for a later time.
+ // Note: It is not necessary to listen for XI_DeviceChanged events.
}
+void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) {
+ Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+
+ // Setup mask for mouse events.
+ unsigned char mask[(XI_LASTEVENT + 7)/8];
+ memset(mask, 0, sizeof(mask));
+
+ XISetMask(mask, XI_ButtonPress);
+ XISetMask(mask, XI_ButtonRelease);
+ XISetMask(mask, XI_Motion);
+
+ // It is necessary to select only for the master devices. XInput2 provides
+ // enough information to the event callback to decide which slave device
+ // triggered the event, thus decide whether the 'pointer event' is a 'mouse
+ // event' or a 'touch event'. So it is not necessary to select for the slave
+ // devices here.
+ XIEventMask evmasks[masters_.size()];
+ int count = 0;
+ for (std::set<int>::const_iterator iter = masters_.begin();
+ iter != masters_.end();
+ ++iter, ++count) {
+ evmasks[count].deviceid = *iter;
+ evmasks[count].mask_len = sizeof(mask);
+ evmasks[count].mask = mask;
+ }
+
+ XISelectEvents(xdisplay, xwindow, evmasks, masters_.size());
+
+ // TODO(sad): Setup masks for keyboard events.
+
+ XFlush(xdisplay);
+}
+
+#endif // HAVE_XINPUT2
+
void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) {
MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data);
diff --git a/base/message_pump_glib_x.h b/base/message_pump_glib_x.h
index 2f50731..c6d98e3 100644
--- a/base/message_pump_glib_x.h
+++ b/base/message_pump_glib_x.h
@@ -9,6 +9,7 @@
#include "base/message_pump_glib.h"
#include <bitset>
+#include <set>
#include <glib.h>
#include <gtk/gtk.h>
@@ -28,6 +29,11 @@ class MessagePumpGlibX : public MessagePumpForUI {
// was captured and being processed by GDK (when |false|).
bool IsDispatchingEvent(void) { return dispatching_event_; }
+#if defined(HAVE_XINPUT2)
+ // Setup an X Window for XInput2 events.
+ void SetupXInput2ForXWindow(Window xid);
+#endif
+
private:
static void EventDispatcherX(GdkEvent* event, gpointer data);
@@ -35,6 +41,22 @@ class MessagePumpGlibX : public MessagePumpForUI {
// processed so that GDK doesn't get to them.
void InitializeEventsToCapture(void);
+#if defined(HAVE_XINPUT2)
+ // Initialize X2 input.
+ void InitializeXInput2(void);
+
+ // The opcode used for checking events.
+ int xiopcode_;
+
+ // The list of master pointer devices. We maintain this list so that it is not
+ // necessary to query X for the list of devices for each GdkWindow created.
+ std::set<int> masters_;
+
+ // The list of slave (physical) pointer devices.
+ // TODO(sad): This is currently unused, and may be removed eventually.
+ std::set<int> slaves_;
+#endif
+
// The event source for GDK events.
GSource* gdksource_;
diff --git a/base/native_library.h b/base/native_library.h
index 2bb8497..3d8c280 100644
--- a/base/native_library.h
+++ b/base/native_library.h
@@ -14,12 +14,12 @@
#if defined(OS_WIN)
#include <windows.h>
#elif defined(OS_MACOSX)
-#import <Carbon/Carbon.h>
+#import <CoreFoundation/CoreFoundation.h>
#endif // OS_*
#include "base/string16.h"
-// Macro usefull for writing cross-platform function pointers.
+// Macro useful for writing cross-platform function pointers.
#if defined(OS_WIN) && !defined(CDECL)
#define CDECL __cdecl
#else
diff --git a/base/native_library_linux.cc b/base/native_library_linux.cc
index b6d7aef..d5ab128 100644
--- a/base/native_library_linux.cc
+++ b/base/native_library_linux.cc
@@ -8,12 +8,16 @@
#include "base/file_path.h"
#include "base/logging.h"
+#include "base/thread_restrictions.h"
#include "base/utf_string_conversions.h"
namespace base {
// static
NativeLibrary LoadNativeLibrary(const FilePath& library_path) {
+ // dlopen() opens the file off disk.
+ base::ThreadRestrictions::AssertIOAllowed();
+
// We deliberately do not use RTLD_DEEPBIND. For the history why, please
// refer to the bug tracker. Some useful bug reports to read include:
// http://crbug.com/17943, http://crbug.com/17557, http://crbug.com/36892,
diff --git a/base/native_library_mac.mm b/base/native_library_mac.mm
index 95cac15..0669bee 100644
--- a/base/native_library_mac.mm
+++ b/base/native_library_mac.mm
@@ -5,18 +5,19 @@
#include "base/native_library.h"
#include <dlfcn.h>
-#import <Carbon/Carbon.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/string_util.h"
+#include "base/thread_restrictions.h"
#include "base/utf_string_conversions.h"
namespace base {
// static
NativeLibrary LoadNativeLibrary(const FilePath& library_path) {
+ // dlopen() etc. open the file off disk.
if (library_path.Extension() == "dylib" ||
!file_util::DirectoryExists(library_path)) {
void* dylib = dlopen(library_path.value().c_str(), RTLD_LAZY);
diff --git a/base/native_library_win.cc b/base/native_library_win.cc
index 94e4b63..b498eba 100644
--- a/base/native_library_win.cc
+++ b/base/native_library_win.cc
@@ -7,12 +7,16 @@
#include <windows.h>
#include "base/file_util.h"
+#include "base/thread_restrictions.h"
#include "base/utf_string_conversions.h"
namespace base {
// static
NativeLibrary LoadNativeLibrary(const FilePath& library_path) {
+ // LoadLibrary() opens the file off disk.
+ base::ThreadRestrictions::AssertIOAllowed();
+
// Switch the current directory to the library directory as the library
// may have dependencies on DLLs in this directory.
bool restore_directory = false;
diff --git a/base/openssl_util.cc b/base/openssl_util.cc
new file mode 100644
index 0000000..894c710
--- /dev/null
+++ b/base/openssl_util.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2010 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/openssl_util.h"
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/scoped_vector.h"
+#include "base/singleton.h"
+
+namespace base {
+
+namespace {
+
+unsigned long CurrentThreadId() {
+ return static_cast<unsigned long>(PlatformThread::CurrentId());
+}
+
+// Singleton for initializing and cleaning up the OpenSSL library.
+class OpenSSLInitSingleton {
+ private:
+ friend struct DefaultSingletonTraits<OpenSSLInitSingleton>;
+ OpenSSLInitSingleton() {
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ int num_locks = CRYPTO_num_locks();
+ locks_.reserve(num_locks);
+ for (int i = 0; i < num_locks; ++i)
+ locks_.push_back(new Lock());
+ CRYPTO_set_locking_callback(LockingCallback);
+ CRYPTO_set_id_callback(CurrentThreadId);
+ }
+
+ ~OpenSSLInitSingleton() {
+ CRYPTO_set_locking_callback(NULL);
+ EVP_cleanup();
+ ERR_free_strings();
+ }
+
+ static void LockingCallback(int mode, int n, const char* file, int line) {
+ Singleton<OpenSSLInitSingleton>::get()->OnLockingCallback(mode, n, file,
+ line);
+ }
+
+ void OnLockingCallback(int mode, int n, const char* file, int line) {
+ CHECK_LT(static_cast<size_t>(n), locks_.size());
+ if (mode & CRYPTO_LOCK)
+ locks_[n]->Acquire();
+ else
+ locks_[n]->Release();
+ }
+
+ // These locks are used and managed by OpenSSL via LockingCallback().
+ ScopedVector<Lock> locks_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton);
+};
+
+} // namespace
+
+void EnsureOpenSSLInit() {
+ (void)Singleton<OpenSSLInitSingleton>::get();
+}
+
+void ClearOpenSSLERRStack() {
+ if (logging::DEBUG_MODE && VLOG_IS_ON(1)) {
+ int error_num = ERR_get_error();
+ if (error_num == 0)
+ return;
+
+ DVLOG(1) << "OpenSSL ERR_get_error stack:";
+ char buf[140];
+ do {
+ ERR_error_string_n(error_num, buf, arraysize(buf));
+ DVLOG(1) << "\t" << error_num << ": " << buf;
+ error_num = ERR_get_error();
+ } while (error_num != 0);
+ } else {
+ ERR_clear_error();
+ }
+}
+
+} // namespace base
diff --git a/base/openssl_util.h b/base/openssl_util.h
index 4f564cf..1d290ae 100644
--- a/base/openssl_util.h
+++ b/base/openssl_util.h
@@ -2,14 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef BASE_OPENNSSL_UTIL_H_
-#define BASE_OPENNSSL_UTIL_H_
+#ifndef BASE_OPENSSL_UTIL_H_
+#define BASE_OPENSSL_UTIL_H_
#pragma once
#include "base/basictypes.h"
+#include "base/tracked.h"
namespace base {
+// A helper class that takes care of destroying OpenSSL objects when it goes out
+// of scope.
+template <typename T, void (*destructor)(T*)>
+class ScopedOpenSSL {
+ public:
+ explicit ScopedOpenSSL(T* ptr_) : ptr_(ptr_) { }
+ ~ScopedOpenSSL() { if (ptr_) (*destructor)(ptr_); }
+
+ T* get() const { return ptr_; }
+
+ private:
+ T* ptr_;
+};
+
// Provides a buffer of at least MIN_SIZE bytes, for use when calling OpenSSL's
// SHA256, HMAC, etc functions, adapting the buffer sizing rules to meet those
// of the our base wrapper APIs.
@@ -46,8 +61,20 @@ class ScopedOpenSSLSafeSizeBuffer {
// Temporary buffer writen into in the case where the caller's
// buffer is not of sufficient size.
unsigned char min_sized_buffer_[MIN_SIZE];
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSLSafeSizeBuffer);
};
+// Initialize OpenSSL if it isn't already initialized. This must be called
+// before any other OpenSSL functions.
+// This function is thread-safe, and OpenSSL will only ever be initialized once.
+// OpenSSL will be properly shut down on program exit.
+void EnsureOpenSSLInit();
+
+// Drains the OpenSSL ERR_get_error stack. On a debug build the error codes
+// are send to VLOG(1), on a release build they are disregarded.
+void ClearOpenSSLERRStack();
+
} // namespace base
-#endif // BASE_NSS_UTIL_H_
+#endif // BASE_OPENSSL_UTIL_H_
diff --git a/base/path_service.cc b/base/path_service.cc
index 8660c42..9eed56d 100644
--- a/base/path_service.cc
+++ b/base/path_service.cc
@@ -215,6 +215,14 @@ bool PathService::Get(int key, std::wstring* result) {
}
#endif
+// TODO(evan): remove me -- see comments in header.
+bool PathService::IsOverridden(int key) {
+ PathData* path_data = GetPathData();
+ DCHECK(path_data);
+ AutoLock scoped_lock(path_data->lock);
+ return path_data->overrides.find(key) != path_data->overrides.end();
+}
+
bool PathService::Override(int key, const FilePath& path) {
PathData* path_data = GetPathData();
DCHECK(path_data);
diff --git a/base/path_service.h b/base/path_service.h
index 4d99cdc..6873e1f 100644
--- a/base/path_service.h
+++ b/base/path_service.h
@@ -45,6 +45,11 @@ class PathService {
// over the lifetime of the app, so this method should be used with caution.
static bool Override(int key, const FilePath& path);
+ // Return whether a path was overridden.
+ // TODO(evan): temporarily restoring this to quick-fix a regression.
+ // Remove me again once it's fixed properly.
+ static bool IsOverridden(int key);
+
// To extend the set of supported keys, you can register a path provider,
// which is just a function mirroring PathService::Get. The ProviderFunc
// returns false if it cannot provide a non-empty path for the given key.
diff --git a/base/pickle.cc b/base/pickle.cc
index 06a3be1..3f376e3 100644
--- a/base/pickle.cc
+++ b/base/pickle.cc
@@ -41,11 +41,21 @@ Pickle::Pickle(int header_size)
Pickle::Pickle(const char* data, int data_len)
: header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
- header_size_(data_len - header_->payload_size),
+ header_size_(0),
capacity_(kCapacityReadOnly),
variable_buffer_offset_(0) {
- DCHECK(header_size_ >= sizeof(Header));
- DCHECK(header_size_ == AlignInt(header_size_, sizeof(uint32)));
+ if (data_len >= static_cast<int>(sizeof(Header)))
+ header_size_ = data_len - header_->payload_size;
+
+ if (header_size_ > static_cast<unsigned int>(data_len))
+ header_size_ = 0;
+
+ if (header_size_ != AlignInt(header_size_, sizeof(uint32)))
+ header_size_ = 0;
+
+ // If there is anything wrong with the data, we're not going to use it.
+ if (!header_size_)
+ header_ = NULL;
}
Pickle::Pickle(const Pickle& other)
diff --git a/base/pickle.h b/base/pickle.h
index c7aee67..6006e62 100644
--- a/base/pickle.h
+++ b/base/pickle.h
@@ -177,10 +177,12 @@ class Pickle {
// Returns the address of the byte immediately following the currently valid
// header + payload.
char* end_of_payload() {
+ // We must have a valid header_.
return payload() + payload_size();
}
const char* end_of_payload() const {
- return payload() + payload_size();
+ // This object may be invalid.
+ return header_ ? payload() + payload_size() : NULL;
}
size_t capacity() const {
diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc
index aea3830..fdc0664 100644
--- a/base/pickle_unittest.cc
+++ b/base/pickle_unittest.cc
@@ -87,6 +87,39 @@ TEST(PickleTest, EncodeDecode) {
VerifyResult(pickle3);
}
+// Tests that we can handle really small buffers.
+TEST(PickleTest, SmallBuffer) {
+ scoped_array<char> buffer(new char[1]);
+
+ // We should not touch the buffer.
+ Pickle pickle(buffer.get(), 1);
+
+ void* iter = NULL;
+ int data;
+ EXPECT_FALSE(pickle.ReadInt(&iter, &data));
+}
+
+// Tests that we can handle improper headers.
+TEST(PickleTest, BigSize) {
+ int buffer[] = { 0x56035200, 25, 40, 50 };
+
+ Pickle pickle(reinterpret_cast<char*>(buffer), sizeof(buffer));
+
+ void* iter = NULL;
+ int data;
+ EXPECT_FALSE(pickle.ReadInt(&iter, &data));
+}
+
+TEST(PickleTest, UnalignedSize) {
+ int buffer[] = { 10, 25, 40, 50 };
+
+ Pickle pickle(reinterpret_cast<char*>(buffer), sizeof(buffer));
+
+ void* iter = NULL;
+ int data;
+ EXPECT_FALSE(pickle.ReadInt(&iter, &data));
+}
+
TEST(PickleTest, ZeroLenStr) {
Pickle pickle;
EXPECT_TRUE(pickle.WriteString(""));
diff --git a/base/platform_thread_posix.cc b/base/platform_thread_posix.cc
index 040452e..66f3928 100644
--- a/base/platform_thread_posix.cc
+++ b/base/platform_thread_posix.cc
@@ -4,7 +4,6 @@
#include "base/platform_thread.h"
-#include <dlfcn.h>
#include <errno.h>
#include <sched.h>
@@ -15,11 +14,16 @@
#endif
#if defined(OS_LINUX)
+#include <dlfcn.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#endif
+#if defined(OS_NACL)
+#include <sys/nacl_syscalls.h>
+#endif
+
#include "base/logging.h"
#include "base/safe_strerror_posix.h"
@@ -47,6 +51,8 @@ PlatformThreadId PlatformThread::CurrentId() {
#elif defined(OS_FREEBSD)
// TODO(BSD): find a better thread ID
return reinterpret_cast<int64>(pthread_self());
+#elif defined(OS_NACL)
+ return pthread_self();
#endif
}
diff --git a/base/process_util.h b/base/process_util.h
index 77d772b..ca43289 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -36,14 +36,6 @@ typedef struct _malloc_zone_t malloc_zone_t;
#include "base/file_descriptor_shuffle.h"
#include "base/process.h"
-#ifndef NAME_MAX // Solaris and some BSDs have no NAME_MAX
-#ifdef MAXNAMLEN
-#define NAME_MAX MAXNAMLEN
-#else
-#define NAME_MAX 256
-#endif
-#endif
-
class CommandLine;
class FilePath;
diff --git a/base/safe_strerror_posix.cc b/base/safe_strerror_posix.cc
index 68b2fca..a91bb8d 100644
--- a/base/safe_strerror_posix.cc
+++ b/base/safe_strerror_posix.cc
@@ -2,13 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "build/build_config.h"
#include "base/safe_strerror_posix.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
-#if defined(__GLIBC__) && defined(__GNUC__)
+#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(OS_NACL))
+
+#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
// GCC will complain about the unused second wrap function unless we tell it
// that we meant for them to be potentially unused, which is exactly what this
// attribute is for.
@@ -17,7 +20,7 @@
#define POSSIBLY_UNUSED
#endif
-#if defined(__GLIBC__)
+#if USE_HISTORICAL_STRERRO_R
// glibc has two strerror_r functions: a historical GNU-specific one that
// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
// that returns int. This wraps the GNU-specific one.
@@ -37,7 +40,7 @@ static void POSSIBLY_UNUSED wrap_posix_strerror_r(
// The GNU version never fails. Unknown errors get an "unknown error" message.
// The result is always null terminated.
}
-#endif // __GLIBC__
+#endif // USE_HISTORICAL_STRERRO_R
// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
// does not define the behaviour for some of the edge cases, so we wrap it to
diff --git a/base/scoped_native_library.cc b/base/scoped_native_library.cc
new file mode 100644
index 0000000..9d34449
--- /dev/null
+++ b/base/scoped_native_library.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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/scoped_native_library.h"
+
+namespace base {
+
+ScopedNativeLibrary::ScopedNativeLibrary() : library_(NULL) {
+}
+
+ScopedNativeLibrary::ScopedNativeLibrary(NativeLibrary library)
+ : library_(library) {
+}
+
+ScopedNativeLibrary::ScopedNativeLibrary(const FilePath& library_path) {
+ library_ = base::LoadNativeLibrary(library_path);
+}
+
+ScopedNativeLibrary::~ScopedNativeLibrary() {
+ if (library_)
+ base::UnloadNativeLibrary(library_);
+}
+
+void* ScopedNativeLibrary::GetFunctionPointer(
+ const char* function_name) const {
+ if (!library_)
+ return NULL;
+ return base::GetFunctionPointerFromNativeLibrary(library_, function_name);
+}
+
+void ScopedNativeLibrary::Reset(NativeLibrary library) {
+ if (library_)
+ base::UnloadNativeLibrary(library_);
+ library_ = library;
+}
+
+NativeLibrary ScopedNativeLibrary::Release() {
+ NativeLibrary result = library_;
+ library_ = NULL;
+ return result;
+}
+
+} // namespace base
diff --git a/base/scoped_native_library.h b/base/scoped_native_library.h
index bed5c39..28b29b3 100644
--- a/base/scoped_native_library.h
+++ b/base/scoped_native_library.h
@@ -17,23 +17,33 @@ namespace base {
// This class automatically unloads the loaded library in its destructor.
class ScopedNativeLibrary {
public:
- explicit ScopedNativeLibrary(const FilePath& library_path) {
- library_ = base::LoadNativeLibrary(library_path);
- }
+ // Initializes with a NULL library.
+ ScopedNativeLibrary();
- ~ScopedNativeLibrary() {
- if (library_)
- base::UnloadNativeLibrary(library_);
- }
+ // Takes ownership of the given library handle.
+ explicit ScopedNativeLibrary(NativeLibrary library);
- void* GetFunctionPointer(const char* function_name) {
- if (!library_)
- return NULL;
- return base::GetFunctionPointerFromNativeLibrary(library_, function_name);
- }
+ // Opens the given library and manages its lifetime.
+ explicit ScopedNativeLibrary(const FilePath& library_path);
+
+ ~ScopedNativeLibrary();
+
+ // Returns true if there's a valid library loaded.
+ bool is_valid() const { return !!library_; }
+
+ void* GetFunctionPointer(const char* function_name) const;
+
+ // Takes ownership of the given library handle. Any existing handle will
+ // be freed.
+ void Reset(NativeLibrary library);
+
+ // Returns the native library handle and removes it from this object. The
+ // caller must manage the lifetime of the handle.
+ NativeLibrary Release();
private:
- base::NativeLibrary library_;
+ NativeLibrary library_;
+
DISALLOW_COPY_AND_ASSIGN(ScopedNativeLibrary);
};
diff --git a/base/scoped_ptr.h b/base/scoped_ptr.h
index 88ee41b..0a90150 100644
--- a/base/scoped_ptr.h
+++ b/base/scoped_ptr.h
@@ -42,8 +42,8 @@
// scoped_array, scoped_ptr_malloc.
#include <assert.h>
+#include <stddef.h>
#include <stdlib.h>
-#include <cstddef>
#include "base/compiler_specific.h"
@@ -193,7 +193,7 @@ class scoped_array {
// Get one element of the current object.
// Will assert() if there is no current object, or index i is negative.
- C& operator[](std::ptrdiff_t i) const {
+ C& operator[](ptrdiff_t i) const {
assert(i >= 0);
assert(array_ != NULL);
return array_[i];
diff --git a/base/scoped_vector.h b/base/scoped_vector.h
index ec152c9..9d372f3 100644
--- a/base/scoped_vector.h
+++ b/base/scoped_vector.h
@@ -54,6 +54,7 @@ class ScopedVector {
}
void reset() { STLDeleteElements(&v); }
+ void reserve(size_t capacity) { v.reserve(capacity); }
void resize(size_t new_size) { v.resize(new_size); }
// Lets the ScopedVector take ownership of |x|.
diff --git a/base/shared_memory.h b/base/shared_memory.h
index 719eb69..a088682 100644
--- a/base/shared_memory.h
+++ b/base/shared_memory.h
@@ -173,9 +173,11 @@ class SharedMemory {
void Lock();
#if defined(OS_WIN)
- // A Lock() implementation with a timeout. Returns true if the Lock() has
- // been acquired, false if the timeout was reached.
- bool Lock(uint32 timeout_ms);
+ // A Lock() implementation with a timeout that also allows setting
+ // security attributes on the mutex. sec_attr may be NULL.
+ // Returns true if the Lock() has been acquired, false if the timeout was
+ // reached.
+ bool Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr);
#endif
// Releases the shared memory lock.
diff --git a/base/shared_memory_win.cc b/base/shared_memory_win.cc
index 5f293fc..15c61dd 100644
--- a/base/shared_memory_win.cc
+++ b/base/shared_memory_win.cc
@@ -197,17 +197,16 @@ void SharedMemory::Close() {
}
void SharedMemory::Lock() {
- Lock(INFINITE);
+ Lock(INFINITE, NULL);
}
-bool SharedMemory::Lock(uint32 timeout_ms) {
+bool SharedMemory::Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr) {
if (lock_ == NULL) {
std::wstring name = name_;
name.append(L"lock");
- lock_ = CreateMutex(NULL, FALSE, name.c_str());
- DCHECK(lock_ != NULL);
+ lock_ = CreateMutex(sec_attr, FALSE, name.c_str());
if (lock_ == NULL) {
- DLOG(ERROR) << "Could not create mutex" << GetLastError();
+ PLOG(ERROR) << "Could not create mutex.";
return false; // there is nothing good we can do here.
}
}
diff --git a/base/singleton.h b/base/singleton.h
index 3fe16ce..564b686 100644
--- a/base/singleton.h
+++ b/base/singleton.h
@@ -239,12 +239,14 @@ class Singleton {
private:
// Adapter function for use with AtExit(). This should be called single
- // threaded, but we might as well take the precautions anyway.
+ // threaded, so don't use atomic operations.
+ // Calling OnExit while singleton is in use by other threads is a mistake.
static void OnExit(void* unused) {
// AtExit should only ever be register after the singleton instance was
// created. We should only ever get here with a valid instance_ pointer.
- Traits::Delete(reinterpret_cast<Type*>(
- base::subtle::NoBarrier_AtomicExchange(&instance_, 0)));
+ Traits::Delete(
+ reinterpret_cast<Type*>(base::subtle::NoBarrier_Load(&instance_)));
+ instance_ = 0;
}
static base::subtle::AtomicWord instance_;
};
diff --git a/base/string16_unittest.cc b/base/string16_unittest.cc
index 06b3dca..e833ed2 100644
--- a/base/string16_unittest.cc
+++ b/base/string16_unittest.cc
@@ -43,7 +43,7 @@ TEST(String16Test, OutputStream) {
std::ostringstream stream;
stream << initial_surrogate << "," << final_surrogate << ","
- << surrogate_pair << ",", unterminated_surrogate;
+ << surrogate_pair << "," << unterminated_surrogate;
EXPECT_STREQ("\xef\xbf\xbd,\xef\xbf\xbd,\xf0\x90\x8c\x80z,\xef\xbf\xbds",
stream.str().c_str());
diff --git a/base/string_split.cc b/base/string_split.cc
index bc7955d..44b5d06 100644
--- a/base/string_split.cc
+++ b/base/string_split.cc
@@ -168,4 +168,61 @@ void SplitStringDontTrim(const std::string& str,
SplitStringT(str, c, false, r);
}
+template<typename STR>
+void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) {
+ const size_t length = str.length();
+ if (!length)
+ return;
+
+ bool last_was_ws = false;
+ size_t last_non_ws_start = 0;
+ for (size_t i = 0; i < length; ++i) {
+ switch (str[i]) {
+ // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR.
+ case L' ':
+ case L'\t':
+ case L'\xA':
+ case L'\xB':
+ case L'\xC':
+ case L'\xD':
+ if (!last_was_ws) {
+ if (i > 0) {
+ result->push_back(
+ str.substr(last_non_ws_start, i - last_non_ws_start));
+ }
+ last_was_ws = true;
+ }
+ break;
+
+ default: // Not a space character.
+ if (last_was_ws) {
+ last_was_ws = false;
+ last_non_ws_start = i;
+ }
+ break;
+ }
+ }
+ if (!last_was_ws) {
+ result->push_back(
+ str.substr(last_non_ws_start, length - last_non_ws_start));
+ }
+}
+
+void SplitStringAlongWhitespace(const std::wstring& str,
+ std::vector<std::wstring>* result) {
+ SplitStringAlongWhitespaceT(str, result);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+void SplitStringAlongWhitespace(const string16& str,
+ std::vector<string16>* result) {
+ SplitStringAlongWhitespaceT(str, result);
+}
+#endif
+
+void SplitStringAlongWhitespace(const std::string& str,
+ std::vector<std::string>* result) {
+ SplitStringAlongWhitespaceT(str, result);
+}
+
} // namespace base
diff --git a/base/string_split.h b/base/string_split.h
index 6af1511..9a9030a 100644
--- a/base/string_split.h
+++ b/base/string_split.h
@@ -69,6 +69,21 @@ void SplitStringDontTrim(const std::string& str,
char c,
std::vector<std::string>* r);
+// WARNING: this uses whitespace as defined by the HTML5 spec. If you need
+// a function similar to this but want to trim all types of whitespace, then
+// factor this out into a function that takes a string containing the characters
+// that are treated as whitespace.
+//
+// Splits the string along whitespace (where whitespace is the five space
+// characters defined by HTML 5). Each contiguous block of non-whitespace
+// characters is added to result.
+void SplitStringAlongWhitespace(const std::wstring& str,
+ std::vector<std::wstring>* result);
+void SplitStringAlongWhitespace(const string16& str,
+ std::vector<string16>* result);
+void SplitStringAlongWhitespace(const std::string& str,
+ std::vector<std::string>* result);
+
} // namespace base
#endif // BASE_STRING_SPLIT_H
diff --git a/base/string_split_unittest.cc b/base/string_split_unittest.cc
index afdc526..749d566 100644
--- a/base/string_split_unittest.cc
+++ b/base/string_split_unittest.cc
@@ -262,4 +262,36 @@ TEST(StringSplitTest, StringSplitDontTrim) {
r.clear();
}
+TEST(StringSplitTest, SplitStringAlongWhitespace) {
+ struct TestData {
+ const std::wstring input;
+ const size_t expected_result_count;
+ const std::wstring output1;
+ const std::wstring output2;
+ } data[] = {
+ { L"a", 1, L"a", L"" },
+ { L" ", 0, L"", L"" },
+ { L" a", 1, L"a", L"" },
+ { L" ab ", 1, L"ab", L"" },
+ { L" ab c", 2, L"ab", L"c" },
+ { L" ab c ", 2, L"ab", L"c" },
+ { L" ab cd", 2, L"ab", L"cd" },
+ { L" ab cd ", 2, L"ab", L"cd" },
+ { L" \ta\t", 1, L"a", L"" },
+ { L" b\ta\t", 2, L"b", L"a" },
+ { L" b\tat", 2, L"b", L"at" },
+ { L"b\tat", 2, L"b", L"at" },
+ { L"b\t at", 2, L"b", L"at" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+ std::vector<std::wstring> results;
+ SplitStringAlongWhitespace(data[i].input, &results);
+ ASSERT_EQ(data[i].expected_result_count, results.size());
+ if (data[i].expected_result_count > 0)
+ ASSERT_EQ(data[i].output1, results[0]);
+ if (data[i].expected_result_count > 1)
+ ASSERT_EQ(data[i].output2, results[1]);
+ }
+}
+
} // namespace base
diff --git a/base/string_util.cc b/base/string_util.cc
index 6717ca5..1c97487 100644
--- a/base/string_util.cc
+++ b/base/string_util.cc
@@ -826,63 +826,6 @@ string16 JoinString(const std::vector<string16>& parts, char16 sep) {
return JoinStringT(parts, sep);
}
-template<typename STR>
-void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) {
- const size_t length = str.length();
- if (!length)
- return;
-
- bool last_was_ws = false;
- size_t last_non_ws_start = 0;
- for (size_t i = 0; i < length; ++i) {
- switch (str[i]) {
- // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR.
- case L' ':
- case L'\t':
- case L'\xA':
- case L'\xB':
- case L'\xC':
- case L'\xD':
- if (!last_was_ws) {
- if (i > 0) {
- result->push_back(
- str.substr(last_non_ws_start, i - last_non_ws_start));
- }
- last_was_ws = true;
- }
- break;
-
- default: // Not a space character.
- if (last_was_ws) {
- last_was_ws = false;
- last_non_ws_start = i;
- }
- break;
- }
- }
- if (!last_was_ws) {
- result->push_back(
- str.substr(last_non_ws_start, length - last_non_ws_start));
- }
-}
-
-void SplitStringAlongWhitespace(const std::wstring& str,
- std::vector<std::wstring>* result) {
- SplitStringAlongWhitespaceT(str, result);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
-void SplitStringAlongWhitespace(const string16& str,
- std::vector<string16>* result) {
- SplitStringAlongWhitespaceT(str, result);
-}
-#endif
-
-void SplitStringAlongWhitespace(const std::string& str,
- std::vector<std::string>* result) {
- SplitStringAlongWhitespaceT(str, result);
-}
-
template<class FormatStringType, class OutStringType>
OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string,
const std::vector<OutStringType>& subst, std::vector<size_t>* offsets) {
diff --git a/base/string_util.h b/base/string_util.h
index c238e4a..498ccc5 100644
--- a/base/string_util.h
+++ b/base/string_util.h
@@ -122,6 +122,12 @@ template <class Char> inline Char ToLowerASCII(Char c) {
return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
}
+// ASCII-specific toupper. The standard library's toupper is locale sensitive,
+// so we don't want to use it here.
+template <class Char> inline Char ToUpperASCII(Char c) {
+ return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+}
+
// Function objects to aid in comparing/searching strings.
template<typename Char> struct CaseInsensitiveCompare {
@@ -295,17 +301,11 @@ template <class str> inline str StringToLowerASCII(const str& s) {
return output;
}
-// ASCII-specific toupper. The standard library's toupper is locale sensitive,
-// so we don't want to use it here.
-template <class Char> inline Char ToUpperASCII(Char c) {
- return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
-}
-
// Converts the elements of the given string. This version uses a pointer to
// clearly differentiate it from the non-pointer variant.
template <class str> inline void StringToUpperASCII(str* s) {
for (typename str::iterator i = s->begin(); i != s->end(); ++i)
- *i = ToUpperASCII(*i);
+ *i = base::ToUpperASCII(*i);
}
template <class str> inline str StringToUpperASCII(const str& s) {
@@ -486,7 +486,6 @@ inline typename string_type::value_type* WriteInto(string_type* str,
//-----------------------------------------------------------------------------
-
// Splits a string into its fields delimited by any of the characters in
// |delimiters|. Each field is added to the |tokens| vector. Returns the
// number of tokens found.
@@ -507,21 +506,6 @@ size_t Tokenize(const base::StringPiece& str,
string16 JoinString(const std::vector<string16>& parts, char16 s);
std::string JoinString(const std::vector<std::string>& parts, char s);
-// WARNING: this uses whitespace as defined by the HTML5 spec. If you need
-// a function similar to this but want to trim all types of whitespace, then
-// factor this out into a function that takes a string containing the characters
-// that are treated as whitespace.
-//
-// Splits the string along whitespace (where whitespace is the five space
-// characters defined by HTML 5). Each contiguous block of non-whitespace
-// characters is added to result.
-void SplitStringAlongWhitespace(const std::wstring& str,
- std::vector<std::wstring>* result);
-void SplitStringAlongWhitespace(const string16& str,
- std::vector<string16>* result);
-void SplitStringAlongWhitespace(const std::string& str,
- std::vector<std::string>* result);
-
// Replace $1-$2-$3..$9 in the format string with |a|-|b|-|c|..|i| respectively.
// Additionally, any number of consecutive '$' characters is replaced by that
// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc
index 0f7d79d..b7639bb 100644
--- a/base/string_util_unittest.cc
+++ b/base/string_util_unittest.cc
@@ -925,38 +925,6 @@ TEST(StringUtilTest, ReplaceStringPlaceholdersConsecutiveDollarSigns) {
"$1 $$2 $$$3");
}
-TEST(StringUtilTest, SplitStringAlongWhitespace) {
- struct TestData {
- const std::wstring input;
- const size_t expected_result_count;
- const std::wstring output1;
- const std::wstring output2;
- } data[] = {
- { L"a", 1, L"a", L"" },
- { L" ", 0, L"", L"" },
- { L" a", 1, L"a", L"" },
- { L" ab ", 1, L"ab", L"" },
- { L" ab c", 2, L"ab", L"c" },
- { L" ab c ", 2, L"ab", L"c" },
- { L" ab cd", 2, L"ab", L"cd" },
- { L" ab cd ", 2, L"ab", L"cd" },
- { L" \ta\t", 1, L"a", L"" },
- { L" b\ta\t", 2, L"b", L"a" },
- { L" b\tat", 2, L"b", L"at" },
- { L"b\tat", 2, L"b", L"at" },
- { L"b\t at", 2, L"b", L"at" },
- };
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
- std::vector<std::wstring> results;
- SplitStringAlongWhitespace(data[i].input, &results);
- ASSERT_EQ(data[i].expected_result_count, results.size());
- if (data[i].expected_result_count > 0)
- ASSERT_EQ(data[i].output1, results[0]);
- if (data[i].expected_result_count > 1)
- ASSERT_EQ(data[i].output2, results[1]);
- }
-}
-
TEST(StringUtilTest, MatchPatternTest) {
EXPECT_TRUE(MatchPattern("www.google.com", "*.com"));
EXPECT_TRUE(MatchPattern("www.google.com", "*"));
diff --git a/base/stringize_macros.h b/base/stringize_macros.h
new file mode 100644
index 0000000..7c3af49
--- /dev/null
+++ b/base/stringize_macros.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2010 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 defines preprocessor macros for stringizing preprocessor
+// symbols (or their output) and manipulating preprocessor symbols
+// that define strings.
+
+#ifndef BASE_STRINGIZE_MACROS_H_
+#define BASE_STRINGIZE_MACROS_H_
+#pragma once
+
+#include "build/build_config.h"
+
+// This is not very useful as it does not expand defined symbols if
+// called directly. Use its counterpart without the _NO_EXPANSION
+// suffix, below.
+#define STRINGIZE_NO_EXPANSION(x) #x
+
+// Use this to quote the provided parameter, first expanding it if it
+// is a preprocessor symbol.
+//
+// For example, if:
+// #define A FOO
+// #define B(x) myobj->FunctionCall(x)
+//
+// Then:
+// STRINGIZE(A) produces "FOO"
+// STRINGIZE(B(y)) produces "myobj->FunctionCall(y)"
+#define STRINGIZE(x) STRINGIZE_NO_EXPANSION(x)
+
+// The following are defined only on Windows (for use when interacting
+// with Windows APIs) as wide strings are otherwise deprecated.
+#if defined(OS_WIN)
+
+// Second-level utility macros to let us expand symbols.
+#define LSTRINGIZE_NO_EXPANSION(x) L ## #x
+#define TO_L_STRING_NO_EXPANSION(x) L ## x
+
+// L version of STRINGIZE(). For examples above,
+// LSTRINGIZE(A) produces L"FOO"
+// LSTRINGIZE(B(y)) produces L"myobj->FunctionCall(y)"
+#define LSTRINGIZE(x) LSTRINGIZE_NO_EXPANSION(x)
+
+// Adds an L in front of an existing ASCII string constant (after
+// expanding symbols). Does not do any quoting.
+//
+// For example, if:
+// #define C "foo"
+//
+// Then:
+// TO_L_STRING(C) produces L"foo"
+#define TO_L_STRING(x) TO_L_STRING_NO_EXPANSION(x)
+
+#endif // defined(OS_WIN)
+
+#endif // BASE_STRINGIZE_MACROS_H_
diff --git a/base/stringize_macros_unittest.cc b/base/stringize_macros_unittest.cc
new file mode 100644
index 0000000..8d92d53
--- /dev/null
+++ b/base/stringize_macros_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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.
+//
+// Unit tests for stringize_macros.h
+
+#include "base/stringize_macros.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Macros as per documentation in header file.
+#define PREPROCESSOR_UTIL_UNITTEST_A FOO
+#define PREPROCESSOR_UTIL_UNITTEST_B(x) myobj->FunctionCall(x)
+#define PREPROCESSOR_UTIL_UNITTEST_C "foo"
+
+TEST(StringizeTest, Ansi) {
+ EXPECT_STREQ(
+ "PREPROCESSOR_UTIL_UNITTEST_A",
+ STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ(
+ "PREPROCESSOR_UTIL_UNITTEST_B(y)",
+ STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ(
+ "PREPROCESSOR_UTIL_UNITTEST_C",
+ STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_C));
+
+ EXPECT_STREQ("FOO", STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ("myobj->FunctionCall(y)",
+ STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ("\"foo\"", STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_C));
+}
+
+#if defined(OS_WIN)
+
+TEST(StringizeTest, Wide) {
+ EXPECT_STREQ(
+ L"PREPROCESSOR_UTIL_UNITTEST_A",
+ LSTRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ(
+ L"PREPROCESSOR_UTIL_UNITTEST_B(y)",
+ LSTRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ(
+ L"PREPROCESSOR_UTIL_UNITTEST_C",
+ LSTRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_C));
+
+ EXPECT_STREQ(L"FOO", LSTRINGIZE(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ(L"myobj->FunctionCall(y)",
+ LSTRINGIZE(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ(L"\"foo\"", LSTRINGIZE(PREPROCESSOR_UTIL_UNITTEST_C));
+}
+
+TEST(ToLStringTest, Main) {
+ EXPECT_STREQ(L"blat", TO_L_STRING_NO_EXPANSION("blat"));
+
+ EXPECT_STREQ(L"foo", TO_L_STRING(PREPROCESSOR_UTIL_UNITTEST_C));
+ EXPECT_STREQ(L"blat", TO_L_STRING("blat"));
+}
+
+#endif // defined(OS_WIN)
diff --git a/base/stringprintf.h b/base/stringprintf.h
index 3608f9d..f3ca6e7 100644
--- a/base/stringprintf.h
+++ b/base/stringprintf.h
@@ -50,7 +50,5 @@ void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap)
// TODO(brettw) remove these when calling code is converted.
using base::StringPrintf;
using base::StringAppendV;
-using base::StringAppendF;
-using base::StringAppendV;
#endif // BASE_STRINGPRINTF_H_
diff --git a/base/thread_local.h b/base/thread_local.h
index 075e209..eba48d2 100644
--- a/base/thread_local.h
+++ b/base/thread_local.h
@@ -57,6 +57,8 @@
namespace base {
+namespace internal {
+
// Helper functions that abstract the cross-platform APIs. Do not use directly.
struct ThreadLocalPlatform {
#if defined(OS_WIN)
@@ -71,27 +73,30 @@ struct ThreadLocalPlatform {
static void SetValueInSlot(SlotType& slot, void* value);
};
+} // namespace internal
+
template <typename Type>
class ThreadLocalPointer {
public:
ThreadLocalPointer() : slot_() {
- ThreadLocalPlatform::AllocateSlot(slot_);
+ internal::ThreadLocalPlatform::AllocateSlot(slot_);
}
~ThreadLocalPointer() {
- ThreadLocalPlatform::FreeSlot(slot_);
+ internal::ThreadLocalPlatform::FreeSlot(slot_);
}
Type* Get() {
- return static_cast<Type*>(ThreadLocalPlatform::GetValueFromSlot(slot_));
+ return static_cast<Type*>(
+ internal::ThreadLocalPlatform::GetValueFromSlot(slot_));
}
void Set(Type* ptr) {
- ThreadLocalPlatform::SetValueInSlot(slot_, ptr);
+ internal::ThreadLocalPlatform::SetValueInSlot(slot_, ptr);
}
private:
- typedef ThreadLocalPlatform::SlotType SlotType;
+ typedef internal::ThreadLocalPlatform::SlotType SlotType;
SlotType slot_;
diff --git a/base/thread_local_posix.cc b/base/thread_local_posix.cc
index 2abfc0e..4d03403 100644
--- a/base/thread_local_posix.cc
+++ b/base/thread_local_posix.cc
@@ -10,6 +10,8 @@
namespace base {
+namespace internal {
+
// static
void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
int error = pthread_key_create(&slot, NULL);
@@ -33,4 +35,6 @@ void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
CHECK_EQ(error, 0);
}
+} // namespace internal
+
} // namespace base
diff --git a/base/thread_local_win.cc b/base/thread_local_win.cc
index 368c0d6..ea14a67 100644
--- a/base/thread_local_win.cc
+++ b/base/thread_local_win.cc
@@ -10,6 +10,8 @@
namespace base {
+namespace internal {
+
// static
void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
slot = TlsAlloc();
@@ -35,4 +37,6 @@ void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
}
}
+} // namespace internal
+
} // namespace base
diff --git a/base/time_mac.cc b/base/time_mac.cc
index 6dfaa20..ad31410 100644
--- a/base/time_mac.cc
+++ b/base/time_mac.cc
@@ -80,8 +80,8 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) {
void Time::Explode(bool is_local, Exploded* exploded) const {
CFAbsoluteTime seconds =
- (static_cast<double>((us_ - kWindowsEpochDeltaMicroseconds) /
- kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970);
+ ((static_cast<double>(us_) - kWindowsEpochDeltaMicroseconds) /
+ kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970;
base::mac::ScopedCFTypeRef<CFTimeZoneRef>
time_zone(is_local ? CFTimeZoneCopySystem() : NULL);
diff --git a/base/time_unittest.cc b/base/time_unittest.cc
index 21e6f89..6ddf4d3 100644
--- a/base/time_unittest.cc
+++ b/base/time_unittest.cc
@@ -52,6 +52,19 @@ TEST(Time, TimeT) {
EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue());
}
+TEST(Time, FromExplodedWithMilliseconds) {
+ // Some platform implementations of FromExploded are liable to drop
+ // milliseconds if we aren't careful.
+ Time now = Time::NowFromSystemTime();
+ Time::Exploded exploded1 = {0};
+ now.UTCExplode(&exploded1);
+ exploded1.millisecond = 500;
+ Time time = Time::FromUTCExploded(exploded1);
+ Time::Exploded exploded2 = {0};
+ time.UTCExplode(&exploded2);
+ EXPECT_EQ(exploded1.millisecond, exploded2.millisecond);
+}
+
TEST(Time, ZeroIsSymmetric) {
Time zero_time(Time::FromTimeT(0));
EXPECT_EQ(0, zero_time.ToTimeT());
diff --git a/base/tuple.h b/base/tuple.h
index b67d924..bfe6562 100644
--- a/base/tuple.h
+++ b/base/tuple.h
@@ -544,6 +544,13 @@ inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) {
template <class ObjT, class Method, class A>
inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) {
+
+#if defined(OS_CHROMEOS) && defined(CHECK)
+ // To troubleshoot crosbug.com/7327.
+ CHECK(obj);
+ CHECK(&arg);
+ CHECK(method);
+#endif
(obj->*method)(arg.a);
}
diff --git a/base/version.cc b/base/version.cc
index 9fbcb6a..fe224eb 100644
--- a/base/version.cc
+++ b/base/version.cc
@@ -63,8 +63,8 @@ int Version::CompareTo(const Version& other) const {
const std::string Version::GetString() const {
DCHECK(is_valid_);
std::string version_str;
- int count = components_.size();
- for (int i = 0; i < count - 1; ++i) {
+ size_t count = components_.size();
+ for (size_t i = 0; i < count - 1; ++i) {
version_str.append(base::IntToString(components_[i]));
version_str.append(".");
}
diff --git a/base/win/i18n.cc b/base/win/i18n.cc
new file mode 100644
index 0000000..59480f2
--- /dev/null
+++ b/base/win/i18n.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2010 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/win/i18n.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace {
+
+// Keep this enum in sync with kLanguageFunctionNames.
+enum LanguageFunction {
+ SYSTEM_LANGUAGES,
+ USER_LANGUAGES,
+ PROCESS_LANGUAGES,
+ THREAD_LANGUAGES,
+ NUM_FUNCTIONS
+};
+
+const char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages";
+const char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages";
+const char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages";
+const char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages";
+
+// Keep this array in sync with enum LanguageFunction.
+const char *const kLanguageFunctionNames[] = {
+ &kSystemLanguagesFunctionName[0],
+ &kUserLanguagesFunctionName[0],
+ &kProcessLanguagesFunctionName[0],
+ &kThreadLanguagesFunctionName[0]
+};
+
+COMPILE_ASSERT(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames),
+ language_function_enum_and_names_out_of_sync);
+
+// Calls one of the MUI Get*PreferredUILanguages functions, placing the result
+// in |languages|. |function| identifies the function to call and |flags| is
+// the function-specific flags (callers must not specify MUI_LANGUAGE_ID or
+// MUI_LANGUAGE_NAME). Returns true if at least one language is placed in
+// |languages|.
+bool GetMUIPreferredUILanguageList(LanguageFunction function, ULONG flags,
+ std::vector<wchar_t>* languages) {
+ DCHECK(0 <= function && NUM_FUNCTIONS > function);
+ DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)));
+ DCHECK(languages);
+
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ if (NULL != kernel32) {
+ typedef BOOL (WINAPI* GetPreferredUILanguages_Fn)(
+ DWORD, PULONG, PZZWSTR, PULONG);
+ GetPreferredUILanguages_Fn get_preferred_ui_languages =
+ reinterpret_cast<GetPreferredUILanguages_Fn>(
+ GetProcAddress(kernel32, kLanguageFunctionNames[function]));
+ if (NULL != get_preferred_ui_languages) {
+ const ULONG call_flags = flags | MUI_LANGUAGE_NAME;
+ ULONG language_count = 0;
+ ULONG buffer_length = 0;
+ if (get_preferred_ui_languages(call_flags, &language_count, NULL,
+ &buffer_length) &&
+ 0 != buffer_length) {
+ languages->resize(buffer_length);
+ if (get_preferred_ui_languages(call_flags, &language_count,
+ &(*languages)[0], &buffer_length) &&
+ 0 != language_count) {
+ DCHECK(languages->size() == buffer_length);
+ return true;
+ } else {
+ DPCHECK(0 == language_count)
+ << "Failed getting preferred UI languages.";
+ }
+ } else {
+ DPCHECK(0 == buffer_length)
+ << "Failed getting size of preferred UI languages.";
+ }
+ } else {
+ VLOG(2) << "MUI not available.";
+ }
+ } else {
+ NOTREACHED() << "kernel32.dll not found.";
+ }
+
+ return false;
+}
+
+bool GetUserDefaultUILanguage(std::wstring* language, std::wstring* region) {
+ DCHECK(language);
+
+ LANGID lang_id = ::GetUserDefaultUILanguage();
+ if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) {
+ const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT);
+ // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9
+ wchar_t result_buffer[9];
+ int result_length =
+ GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0],
+ arraysize(result_buffer));
+ DPCHECK(0 != result_length) << "Failed getting language id";
+ if (1 < result_length) {
+ language->assign(&result_buffer[0], result_length - 1);
+ region->clear();
+ if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) {
+ result_length =
+ GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0],
+ arraysize(result_buffer));
+ DPCHECK(0 != result_length) << "Failed getting region id";
+ if (1 < result_length)
+ region->assign(&result_buffer[0], result_length - 1);
+ }
+ return true;
+ }
+ } else {
+ // This is entirely unexpected on pre-Vista, which is the only time we
+ // should try GetUserDefaultUILanguage anyway.
+ NOTREACHED() << "Cannot determine language for a supplemental locale.";
+ }
+ return false;
+}
+
+bool GetPreferredUILanguageList(LanguageFunction function, ULONG flags,
+ std::vector<std::wstring>* languages) {
+ std::vector<wchar_t> buffer;
+ std::wstring language;
+ std::wstring region;
+
+ if (GetMUIPreferredUILanguageList(function, flags, &buffer)) {
+ std::vector<wchar_t>::const_iterator scan = buffer.begin();
+ language.assign(&*scan);
+ while (!language.empty()) {
+ languages->push_back(language);
+ scan += language.size() + 1;
+ language.assign(&*scan);
+ }
+ } else if (GetUserDefaultUILanguage(&language, &region)) {
+ // Mimic the MUI behavior of putting the neutral version of the lang after
+ // the regional one (e.g., "fr-CA, fr").
+ if (!region.empty())
+ languages->push_back(std::wstring(language)
+ .append(1, L'-')
+ .append(region));
+ languages->push_back(language);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace base {
+namespace win {
+namespace i18n {
+
+bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) {
+ DCHECK(languages);
+ return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages);
+}
+
+bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) {
+ DCHECK(languages);
+ return GetPreferredUILanguageList(
+ THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK,
+ languages);
+}
+
+} // namespace i18n
+} // namespace win
+} // namespace base
diff --git a/base/win/i18n.h b/base/win/i18n.h
new file mode 100644
index 0000000..ba0f74d
--- /dev/null
+++ b/base/win/i18n.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2010 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.
+
+#ifndef BASE_WIN_I18N_H_
+#define BASE_WIN_I18N_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+namespace i18n {
+
+// Adds to |languages| the list of user preferred UI languages from MUI, if
+// available, falling-back on the user default UI language otherwise. Returns
+// true if at least one language is added.
+bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages);
+
+// Adds to |languages| the list of thread, process, user, and system preferred
+// UI languages from MUI, if available, falling-back on the user default UI
+// language otherwise. Returns true if at least one language is added.
+bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages);
+
+} // namespace i18n
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_I18N_H_
diff --git a/base/win/i18n_unittest.cc b/base/win/i18n_unittest.cc
new file mode 100644
index 0000000..781fc39
--- /dev/null
+++ b/base/win/i18n_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 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 contains unit tests for Windows internationalization funcs.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/win/i18n.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+namespace win {
+namespace i18n {
+
+// Tests that at least one user preferred UI language can be obtained.
+TEST(I18NTest, GetUserPreferredUILanguageList) {
+ std::vector<std::wstring> languages;
+ EXPECT_TRUE(GetUserPreferredUILanguageList(&languages));
+ EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0),
+ languages.size());
+ for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
+ end = languages.end(); scan != end; ++scan) {
+ EXPECT_FALSE((*scan).empty());
+ }
+}
+
+// Tests that at least one thread preferred UI language can be obtained.
+TEST(I18NTest, GetThreadPreferredUILanguageList) {
+ std::vector<std::wstring> languages;
+ EXPECT_TRUE(GetThreadPreferredUILanguageList(&languages));
+ EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0),
+ languages.size());
+ for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
+ end = languages.end(); scan != end; ++scan) {
+ EXPECT_FALSE((*scan).empty());
+ }
+}
+
+} // namespace i18n
+} // namespace win
+} // namespace base