diff options
author | Ben Murdoch <benm@google.com> | 2010-11-25 19:40:10 +0000 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-12-03 13:52:53 +0000 |
commit | 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7 (patch) | |
tree | 938665d93a11fe7a6d0124e3c1e020d1f9d3f947 /base | |
parent | 7c627d87728a355737862918d144f98f69406954 (diff) | |
download | external_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')
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, ®ion)) { + // 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 |