Import Chromium sources r15462

This commit is contained in:
Benjamin Smedberg 2009-06-29 08:35:58 -04:00
Родитель 8bfb4369c2
Коммит a64afe22b9
784 изменённых файлов: 211093 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,6 @@
include_rules = [
"+third_party/zlib",
"+third_party/libevent",
"+third_party/libjpeg",
"+third_party/dmg_fp",
]

Просмотреть файл

@ -0,0 +1,67 @@
// Copyright (c) 2006-2008 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/at_exit.h"
#include "base/logging.h"
namespace base {
// Keep a stack of registered AtExitManagers. We always operate on the most
// recent, and we should never have more than one outside of testing, when we
// use the shadow version of the constructor. We don't protect this for
// thread-safe access, since it will only be modified in testing.
static AtExitManager* g_top_manager = NULL;
AtExitManager::AtExitManager() : next_manager_(NULL) {
DCHECK(!g_top_manager);
g_top_manager = this;
}
AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
DCHECK(shadow || !g_top_manager);
g_top_manager = this;
}
AtExitManager::~AtExitManager() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
return;
}
DCHECK(g_top_manager == this);
ProcessCallbacksNow();
g_top_manager = next_manager_;
}
// static
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
if (!g_top_manager) {
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
return;
}
DCHECK(func);
AutoLock lock(g_top_manager->lock_);
g_top_manager->stack_.push(CallbackAndParam(func, param));
}
// static
void AtExitManager::ProcessCallbacksNow() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
return;
}
AutoLock lock(g_top_manager->lock_);
while (!g_top_manager->stack_.empty()) {
CallbackAndParam callback_and_param = g_top_manager->stack_.top();
g_top_manager->stack_.pop();
callback_and_param.func_(callback_and_param.param_);
}
}
} // namespace base

Просмотреть файл

@ -0,0 +1,71 @@
// Copyright (c) 2006-2008 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_AT_EXIT_H_
#define BASE_AT_EXIT_H_
#include <stack>
#include "base/basictypes.h"
#include "base/lock.h"
namespace base {
// This class provides a facility similar to the CRT atexit(), except that
// we control when the callbacks are executed. Under Windows for a DLL they
// happen at a really bad time and under the loader lock. This facility is
// mostly used by base::Singleton.
//
// The usage is simple. Early in the main() or WinMain() scope create an
// AtExitManager object on the stack:
// int main(...) {
// base::AtExitManager exit_manager;
//
// }
// When the exit_manager object goes out of scope, all the registered
// callbacks and singleton destructors will be called.
class AtExitManager {
protected:
// This constructor will allow this instance of AtExitManager to be created
// even if one already exists. This should only be used for testing!
// AtExitManagers are kept on a global stack, and it will be removed during
// destruction. This allows you to shadow another AtExitManager.
AtExitManager(bool shadow);
public:
typedef void (*AtExitCallbackType)(void*);
AtExitManager();
// The dtor calls all the registered callbacks. Do not try to register more
// callbacks after this point.
~AtExitManager();
// Registers the specified function to be called at exit. The prototype of
// the callback function is void func().
static void RegisterCallback(AtExitCallbackType func, void* param);
// Calls the functions registered with RegisterCallback in LIFO order. It
// is possible to register new callbacks after calling this function.
static void ProcessCallbacksNow();
private:
struct CallbackAndParam {
CallbackAndParam(AtExitCallbackType func, void* param)
: func_(func), param_(param) { }
AtExitCallbackType func_;
void* param_;
};
Lock lock_;
std::stack<CallbackAndParam> stack_;
AtExitManager* next_manager_; // Stack of managers to allow shadowing.
DISALLOW_COPY_AND_ASSIGN(AtExitManager);
};
} // namespace base
#endif // BASE_AT_EXIT_H_

Просмотреть файл

@ -0,0 +1,85 @@
// Copyright (c) 2006-2008 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/at_exit.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Don't test the global AtExitManager, because asking it to process its
// AtExit callbacks can ruin the global state that other tests may depend on.
class ShadowingAtExitManager : public base::AtExitManager {
public:
ShadowingAtExitManager() : AtExitManager(true) {}
};
int g_test_counter_1 = 0;
int g_test_counter_2 = 0;
void IncrementTestCounter1(void* unused) {
++g_test_counter_1;
}
void IncrementTestCounter2(void* unused) {
++g_test_counter_2;
}
void ZeroTestCounters() {
g_test_counter_1 = 0;
g_test_counter_2 = 0;
}
void ExpectCounter1IsZero(void* unused) {
EXPECT_EQ(0, g_test_counter_1);
}
void ExpectParamIsNull(void* param) {
EXPECT_EQ(static_cast<void*>(NULL), param);
}
void ExpectParamIsCounter(void* param) {
EXPECT_EQ(&g_test_counter_1, param);
}
} // namespace
TEST(AtExitTest, Basic) {
ShadowingAtExitManager shadowing_at_exit_manager;
ZeroTestCounters();
base::AtExitManager::RegisterCallback(&IncrementTestCounter1, NULL);
base::AtExitManager::RegisterCallback(&IncrementTestCounter2, NULL);
base::AtExitManager::RegisterCallback(&IncrementTestCounter1, NULL);
EXPECT_EQ(0, g_test_counter_1);
EXPECT_EQ(0, g_test_counter_2);
base::AtExitManager::ProcessCallbacksNow();
EXPECT_EQ(2, g_test_counter_1);
EXPECT_EQ(1, g_test_counter_2);
}
TEST(AtExitTest, LIFOOrder) {
ShadowingAtExitManager shadowing_at_exit_manager;
ZeroTestCounters();
base::AtExitManager::RegisterCallback(&IncrementTestCounter1, NULL);
base::AtExitManager::RegisterCallback(&ExpectCounter1IsZero, NULL);
base::AtExitManager::RegisterCallback(&IncrementTestCounter2, NULL);
EXPECT_EQ(0, g_test_counter_1);
EXPECT_EQ(0, g_test_counter_2);
base::AtExitManager::ProcessCallbacksNow();
EXPECT_EQ(1, g_test_counter_1);
EXPECT_EQ(1, g_test_counter_2);
}
TEST(AtExitTest, Param) {
ShadowingAtExitManager shadowing_at_exit_manager;
base::AtExitManager::RegisterCallback(&ExpectParamIsNull, NULL);
base::AtExitManager::RegisterCallback(&ExpectParamIsCounter,
&g_test_counter_1);
base::AtExitManager::ProcessCallbacksNow();
}

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) 2006-2008 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 is a low level implementation of atomic semantics for reference
// counting. Please use base/ref_counted.h directly instead.
#ifndef BASE_ATOMIC_REF_COUNT_H_
#define BASE_ATOMIC_REF_COUNT_H_
#include "base/atomicops.h"
namespace base {
typedef subtle::Atomic32 AtomicRefCount;
// Increment a reference count by "increment", which must exceed 0.
inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr,
AtomicRefCount increment) {
subtle::NoBarrier_AtomicIncrement(ptr, increment);
}
// Decrement a reference count by "decrement", which must exceed 0,
// and return whether the result is non-zero.
// Insert barriers to ensure that state written before the reference count
// became zero will be visible to a thread that has just made the count zero.
inline bool AtomicRefCountDecN(volatile AtomicRefCount *ptr,
AtomicRefCount decrement) {
return subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0;
}
// Increment a reference count by 1.
inline void AtomicRefCountInc(volatile AtomicRefCount *ptr) {
base::AtomicRefCountIncN(ptr, 1);
}
// Decrement a reference count by 1 and return whether the result is non-zero.
// Insert barriers to ensure that state written before the reference count
// became zero will be visible to a thread that has just made the count zero.
inline bool AtomicRefCountDec(volatile AtomicRefCount *ptr) {
return base::AtomicRefCountDecN(ptr, 1);
}
// Return whether the reference count is one. If the reference count is used
// in the conventional way, a refrerence count of 1 implies that the current
// thread owns the reference and no other thread shares it. This call performs
// the test for a reference count of one, and performs the memory barrier
// needed for the owning thread to act on the object, knowing that it has
// exclusive access to the object.
inline bool AtomicRefCountIsOne(volatile AtomicRefCount *ptr) {
return subtle::Acquire_Load(ptr) == 1;
}
// Return whether the reference count is zero. With conventional object
// referencing counting, the object will be destroyed, so the reference count
// should never be zero. Hence this is generally used for a debug check.
inline bool AtomicRefCountIsZero(volatile AtomicRefCount *ptr) {
return subtle::Acquire_Load(ptr) == 0;
}
} // namespace base
#endif // BASE_ATOMIC_REF_COUNT_H_

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) 2006-2008 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_ATOMIC_SEQUENCE_NUM_H_
#define BASE_ATOMIC_SEQUENCE_NUM_H_
#include "base/atomicops.h"
#include "base/basictypes.h"
namespace base {
class AtomicSequenceNumber {
public:
AtomicSequenceNumber() : seq_(0) { }
explicit AtomicSequenceNumber(base::LinkerInitialized x) { /* seq_ is 0 */ }
int GetNext() {
return static_cast<int>(
base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1);
}
private:
base::subtle::Atomic32 seq_;
DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber);
};
} // namespace base
#endif // BASE_ATOMIC_SEQUENCE_NUM_H_

Просмотреть файл

@ -0,0 +1,139 @@
// Copyright (c) 2006-2008 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.
// For atomic operations on reference counts, see atomic_refcount.h.
// For atomic operations on sequence numbers, see atomic_sequence_num.h.
// The routines exported by this module are subtle. If you use them, even if
// you get the code right, it will depend on careful reasoning about atomicity
// and memory ordering; it will be less readable, and harder to maintain. If
// you plan to use these routines, you should have a good reason, such as solid
// evidence that performance would otherwise suffer, or there being no
// alternative. You should assume only properties explicitly guaranteed by the
// specifications in this file. You are almost certainly _not_ writing code
// just for the x86; if you assume x86 semantics, x86 hardware bugs and
// implementations on other archtectures will cause your code to break. If you
// do not know what you are doing, avoid these routines, and use a Mutex.
//
// It is incorrect to make direct assignments to/from an atomic variable.
// You should use one of the Load or Store routines. The NoBarrier
// versions are provided when no barriers are needed:
// NoBarrier_Store()
// NoBarrier_Load()
// Although there are currently no compiler enforcement, you are encouraged
// to use these.
//
#ifndef BASE_ATOMICOPS_H_
#define BASE_ATOMICOPS_H_
#include "base/basictypes.h"
#include "base/port.h"
namespace base {
namespace subtle {
// Bug 1308991. We need this for /Wp64, to mark it safe for AtomicWord casting.
#ifndef OS_WIN
#define __w64
#endif
typedef __w64 int32 Atomic32;
#ifdef CPU_ARCH_64_BITS
typedef int64 Atomic64;
#endif
// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
// Atomic64 routines below, depending on your architecture.
typedef intptr_t AtomicWord;
// Atomically execute:
// result = *ptr;
// if (*ptr == old_value)
// *ptr = new_value;
// return result;
//
// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
// Always return the old value of "*ptr"
//
// This routine implies no memory barriers.
Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
// Atomically store new_value into *ptr, returning the previous value held in
// *ptr. This routine implies no memory barriers.
Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
// Atomically increment *ptr by "increment". Returns the new value of
// *ptr with the increment applied. This routine implies no memory barriers.
Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment);
Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment);
// These following lower-level operations are typically useful only to people
// implementing higher-level synchronization operations like spinlocks,
// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or
// a store with appropriate memory-ordering instructions. "Acquire" operations
// ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
// access.
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
void MemoryBarrier();
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
void Release_Store(volatile Atomic32* ptr, Atomic32 value);
Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
Atomic32 Acquire_Load(volatile const Atomic32* ptr);
Atomic32 Release_Load(volatile const Atomic32* ptr);
// 64-bit atomic operations (only available on 64-bit processors).
#ifdef CPU_ARCH_64_BITS
Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value);
void Acquire_Store(volatile Atomic64* ptr, Atomic64 value);
void Release_Store(volatile Atomic64* ptr, Atomic64 value);
Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
Atomic64 Acquire_Load(volatile const Atomic64* ptr);
Atomic64 Release_Load(volatile const Atomic64* ptr);
#endif // CPU_ARCH_64_BITS
} // namespace base::subtle
} // namespace base
// Include our platform specific implementation.
#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)
#include "base/atomicops_internals_x86_msvc.h"
#elif defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
#include "base/atomicops_internals_x86_macosx.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
#include "base/atomicops_internals_x86_gcc.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
#include "base/atomicops_internals_arm_gcc.h"
#else
#error "Atomic operations are not supported on your platform"
#endif
#endif // BASE_ATOMICOPS_H_

Просмотреть файл

@ -0,0 +1,124 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use base/atomicops.h instead.
//
// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
namespace base {
namespace subtle {
// 0xffff0fc0 is the hard coded address of a function provided by
// the kernel which implements an atomic compare-exchange. On older
// ARM architecture revisions (pre-v6) this may be implemented using
// a syscall. This address is stable, and in active use (hard coded)
// by at least glibc-2.7 and the Android C library.
typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value,
Atomic32 new_value,
volatile Atomic32* ptr);
LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) =
(LinuxKernelCmpxchgFunc) 0xffff0fc0;
typedef void (*LinuxKernelMemoryBarrierFunc)(void);
LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
(LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value = *ptr;
do {
if (!pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
for (;;) {
// Atomic exchange the old value with an incremented one.
Atomic32 old_value = *ptr;
Atomic32 new_value = old_value + increment;
if (pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr)) == 0) {
// The exchange took place as expected.
return new_value;
}
// Otherwise, *ptr changed mid-loop and we need to retry.
}
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void MemoryBarrier() {
pLinuxKernelMemoryBarrier();
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_

Просмотреть файл

@ -0,0 +1,104 @@
// Copyright (c) 2006-2008 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 module gets enough CPU information to optimize the
// atomicops module on x86.
#include <string.h>
#include "base/atomicops.h"
#include "base/basictypes.h"
// This file only makes sense with atomicops_internals_x86_gcc.h -- it
// depends on structs that are defined in that file. If atomicops.h
// doesn't sub-include that file, then we aren't needed, and shouldn't
// try to do anything.
#ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
// Inline cpuid instruction. In PIC compilations, %ebx contains the address
// of the global offset table. To avoid breaking such executables, this code
// must preserve that register's value across cpuid instructions.
#if defined(__i386__)
#define cpuid(a, b, c, d, inp) \
asm ("mov %%ebx, %%edi\n" \
"cpuid\n" \
"xchg %%edi, %%ebx\n" \
: "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
#elif defined (__x86_64__)
#define cpuid(a, b, c, d, inp) \
asm ("mov %%rbx, %%rdi\n" \
"cpuid\n" \
"xchg %%rdi, %%rbx\n" \
: "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
#endif
#if defined(cpuid) // initialize the struct only on x86
// Set the flags so that code will run correctly and conservatively, so even
// if we haven't been initialized yet, we're probably single threaded, and our
// default values should hopefully be pretty safe.
struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
false, // bug can't exist before process spawns multiple threads
false, // no SSE2
};
// Initialize the AtomicOps_Internalx86CPUFeatures struct.
static void AtomicOps_Internalx86CPUFeaturesInit() {
uint32 eax;
uint32 ebx;
uint32 ecx;
uint32 edx;
// Get vendor string (issue CPUID with eax = 0)
cpuid(eax, ebx, ecx, edx, 0);
char vendor[13];
memcpy(vendor, &ebx, 4);
memcpy(vendor + 4, &edx, 4);
memcpy(vendor + 8, &ecx, 4);
vendor[12] = 0;
// get feature flags in ecx/edx, and family/model in eax
cpuid(eax, ebx, ecx, edx, 1);
int family = (eax >> 8) & 0xf; // family and model fields
int model = (eax >> 4) & 0xf;
if (family == 0xf) { // use extended family and model fields
family += (eax >> 20) & 0xff;
model += ((eax >> 16) & 0xf) << 4;
}
// Opteron Rev E has a bug in which on very rare occasions a locked
// instruction doesn't act as a read-acquire barrier if followed by a
// non-locked read-modify-write instruction. Rev F has this bug in
// pre-release versions, but not in versions released to customers,
// so we test only for Rev E, which is family 15, model 32..63 inclusive.
if (strcmp(vendor, "AuthenticAMD") == 0 && // AMD
family == 15 &&
32 <= model && model <= 63) {
AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true;
} else {
AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false;
}
// edx bit 26 is SSE2 which we use to tell use whether we can use mfence
AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1);
}
namespace {
class AtomicOpsx86Initializer {
public:
AtomicOpsx86Initializer() {
AtomicOps_Internalx86CPUFeaturesInit();
}
};
// A global to get use initialized on startup via static initialization :/
AtomicOpsx86Initializer g_initer;
} // namespace
#endif // if x86
#endif // ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_

Просмотреть файл

@ -0,0 +1,248 @@
// Copyright (c) 2006-2008 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 is an internal atomic implementation, use base/atomicops.h instead.
#ifndef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
#define BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
// This struct is not part of the public API of this module; clients may not
// use it.
// Features of this x86. Values may not be correct before main() is run,
// but are set conservatively.
struct AtomicOps_x86CPUFeatureStruct {
bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence
// after acquire compare-and-swap.
bool has_sse2; // Processor has SSE2.
};
extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
namespace base {
namespace subtle {
// 32-bit low-level operations on any platform.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev;
__asm__ __volatile__("lock; cmpxchgl %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
__asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
: "=r" (new_value)
: "m" (*ptr), "0" (new_value)
: "memory");
return new_value; // Now it's the previous value.
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
Atomic32 temp = increment;
__asm__ __volatile__("lock; xaddl %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now holds the old value of *ptr
return temp + increment;
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
Atomic32 temp = increment;
__asm__ __volatile__("lock; xaddl %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now holds the old value of *ptr
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return temp + increment;
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return x;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
#if defined(__x86_64__)
// 64-bit implementations of memory barrier can be simpler, because it
// "mfence" is guaranteed to exist.
inline void MemoryBarrier() {
__asm__ __volatile__("mfence" : : : "memory");
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
#else
inline void MemoryBarrier() {
if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
__asm__ __volatile__("mfence" : : : "memory");
} else { // mfence is faster but not present on PIII
Atomic32 x = 0;
NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII
}
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
*ptr = value;
__asm__ __volatile__("mfence" : : : "memory");
} else {
NoBarrier_AtomicExchange(ptr, value);
// acts as a barrier on PIII
}
}
#endif
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
ATOMICOPS_COMPILER_BARRIER();
*ptr = value; // An x86 store acts as a release barrier.
// See comments in Atomic64 version of Release_Store(), below.
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr; // An x86 load acts as a acquire barrier.
// See comments in Atomic64 version of Release_Store(), below.
ATOMICOPS_COMPILER_BARRIER();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
#if defined(__x86_64__)
// 64-bit low-level operations on 64-bit platform.
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
__asm__ __volatile__("lock; cmpxchgq %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
__asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg.
: "=r" (new_value)
: "m" (*ptr), "0" (new_value)
: "memory");
return new_value; // Now it's the previous value.
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
Atomic64 temp = increment;
__asm__ __volatile__("lock; xaddq %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now contains the previous value of *ptr
return temp + increment;
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
Atomic64 temp = increment;
__asm__ __volatile__("lock; xaddq %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now contains the previous value of *ptr
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return temp + increment;
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
ATOMICOPS_COMPILER_BARRIER();
*ptr = value; // An x86 store acts as a release barrier
// for current AMD/Intel chips as of Jan 2008.
// See also Acquire_Load(), below.
// When new chips come out, check:
// IA-32 Intel Architecture Software Developer's Manual, Volume 3:
// System Programming Guide, Chatper 7: Multiple-processor management,
// Section 7.2, Memory Ordering.
// Last seen at:
// http://developer.intel.com/design/pentium4/manuals/index_new.htm
//
// x86 stores/loads fail to act as barriers for a few instructions (clflush
// maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
// not generated by the compiler, and are rare. Users of these instructions
// need to know about cache behaviour in any case since all of these involve
// either flushing cache lines or non-temporal cache hints.
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr; // An x86 load acts as a acquire barrier,
// for current AMD/Intel chips as of Jan 2008.
// See also Release_Store(), above.
ATOMICOPS_COMPILER_BARRIER();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
#endif // defined(__x86_64__)
} // namespace base::subtle
} // namespace base
#undef ATOMICOPS_COMPILER_BARRIER
#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_

Просмотреть файл

@ -0,0 +1,279 @@
// Copyright (c) 2006-2008 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 is an internal atomic implementation, use base/atomicops.h instead.
#ifndef BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_
#define BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_
#include <libkern/OSAtomic.h>
namespace base {
namespace subtle {
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
Atomic32 increment) {
return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr));
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
Atomic32 increment) {
return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr));
}
inline void MemoryBarrier() {
OSMemoryBarrier();
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32Barrier(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
MemoryBarrier();
return *ptr;
}
#ifdef __LP64__
// 64-bit implementation on 64-bit platform
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
Atomic64 increment) {
return OSAtomicAdd64(increment, const_cast<Atomic64*>(ptr));
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
Atomic64 increment) {
return OSAtomicAdd64Barrier(increment, const_cast<Atomic64*>(ptr));
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64Barrier(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
// The lib kern interface does not distinguish between
// Acquire and Release memory barriers; they are equivalent.
return Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
MemoryBarrier();
return *ptr;
}
#endif // defined(__LP64__)
// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different
// on the Mac, even when they are the same size. We need to explicitly cast
// from AtomicWord to Atomic32/64 to implement the AtomicWord interface.
#ifdef __LP64__
#define AtomicWordCastType Atomic64
#else
#define AtomicWordCastType Atomic32
#endif
inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return NoBarrier_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
AtomicWord new_value) {
return NoBarrier_AtomicExchange(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value);
}
inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr,
AtomicWord increment) {
return NoBarrier_AtomicIncrement(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment);
}
inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr,
AtomicWord increment) {
return Barrier_AtomicIncrement(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment);
}
inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Acquire_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Release_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) {
NoBarrier_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) {
return base::subtle::Acquire_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
return base::subtle::Release_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) {
return NoBarrier_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
return base::subtle::Acquire_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
return base::subtle::Release_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
#undef AtomicWordCastType
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_

Просмотреть файл

@ -0,0 +1,167 @@
// Copyright (c) 2006-2008 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 is an internal atomic implementation, use base/atomicops.h instead.
#ifndef BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
#define BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
#include <windows.h>
namespace base {
namespace subtle {
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
LONG result = InterlockedCompareExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value),
static_cast<LONG>(old_value));
return static_cast<Atomic32>(result);
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
LONG result = InterlockedExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value));
return static_cast<Atomic32>(result);
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return InterlockedExchangeAdd(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(increment)) + increment;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
#error "We require at least vs2005 for MemoryBarrier"
#endif
inline void MemoryBarrier() {
// We use MemoryBarrier from WinNT.h
::MemoryBarrier();
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
NoBarrier_AtomicExchange(ptr, value);
// acts as a barrier in this implementation
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value; // works w/o barrier for current Intel chips as of June 2005
// See comments in Atomic64 version of Release_Store() below.
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
#if defined(_WIN64)
// 64-bit low-level operations on 64-bit platform.
COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
PVOID result = InterlockedCompareExchangePointer(
reinterpret_cast<volatile PVOID*>(ptr),
reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
return reinterpret_cast<Atomic64>(result);
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
PVOID result = InterlockedExchangePointer(
reinterpret_cast<volatile PVOID*>(ptr),
reinterpret_cast<PVOID>(new_value));
return reinterpret_cast<Atomic64>(result);
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return InterlockedExchangeAdd64(
reinterpret_cast<volatile LONGLONG*>(ptr),
static_cast<LONGLONG>(increment)) + increment;
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
NoBarrier_AtomicExchange(ptr, value);
// acts as a barrier in this implementation
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value; // works w/o barrier for current Intel chips as of June 2005
// When new chips come out, check:
// IA-32 Intel Architecture Software Developer's Manual, Volume 3:
// System Programming Guide, Chatper 7: Multiple-processor management,
// Section 7.2, Memory Ordering.
// Last seen at:
// http://developer.intel.com/design/pentium4/manuals/index_new.htm
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
#endif // defined(_WIN64)
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_

Просмотреть файл

@ -0,0 +1,237 @@
// Copyright (c) 2006-2008 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/atomicops.h"
#include "testing/gtest/include/gtest/gtest.h"
template <class AtomicType>
static void TestAtomicIncrement() {
// For now, we just test single threaded execution
// use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go
// outside the expected address bounds. This is in particular to
// test that some future change to the asm code doesn't cause the
// 32-bit NoBarrier_AtomicIncrement doesn't do the wrong thing on 64-bit
// machines.
struct {
AtomicType prev_word;
AtomicType count;
AtomicType next_word;
} s;
AtomicType prev_word_value, next_word_value;
memset(&prev_word_value, 0xFF, sizeof(AtomicType));
memset(&next_word_value, 0xEE, sizeof(AtomicType));
s.prev_word = prev_word_value;
s.count = 0;
s.next_word = next_word_value;
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1);
EXPECT_EQ(s.count, 1);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3);
EXPECT_EQ(s.count, 3);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6);
EXPECT_EQ(s.count, 6);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3);
EXPECT_EQ(s.count, 3);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1);
EXPECT_EQ(s.count, 1);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0);
EXPECT_EQ(s.count, 0);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1);
EXPECT_EQ(s.count, -1);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5);
EXPECT_EQ(s.count, -5);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0);
EXPECT_EQ(s.count, 0);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
}
#define NUM_BITS(T) (sizeof(T) * 8)
template <class AtomicType>
static void TestCompareAndSwap() {
AtomicType value = 0;
AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1);
EXPECT_EQ(1, value);
EXPECT_EQ(0, prev);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5);
EXPECT_EQ(k_test_val, value);
EXPECT_EQ(k_test_val, prev);
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5);
EXPECT_EQ(5, value);
EXPECT_EQ(k_test_val, prev);
}
template <class AtomicType>
static void TestAtomicExchange() {
AtomicType value = 0;
AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1);
EXPECT_EQ(1, value);
EXPECT_EQ(0, new_value);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val);
EXPECT_EQ(k_test_val, value);
EXPECT_EQ(k_test_val, new_value);
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5);
EXPECT_EQ(5, value);
EXPECT_EQ(k_test_val, new_value);
}
template <class AtomicType>
static void TestAtomicIncrementBounds() {
// Test at rollover boundary between int_max and int_min
AtomicType test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 1));
AtomicType value = -1 ^ test_val;
AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
EXPECT_EQ(test_val, value);
EXPECT_EQ(value, new_value);
base::subtle::NoBarrier_AtomicIncrement(&value, -1);
EXPECT_EQ(-1 ^ test_val, value);
// Test at 32-bit boundary for 64-bit atomic type.
test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2);
value = test_val - 1;
new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
EXPECT_EQ(test_val, value);
EXPECT_EQ(value, new_value);
base::subtle::NoBarrier_AtomicIncrement(&value, -1);
EXPECT_EQ(test_val - 1, value);
}
// Return an AtomicType with the value 0xa5a5a5..
template <class AtomicType>
static AtomicType TestFillValue() {
AtomicType val = 0;
memset(&val, 0xa5, sizeof(AtomicType));
return val;
}
// This is a simple sanity check that values are correct. Not testing
// atomicity
template <class AtomicType>
static void TestStore() {
const AtomicType kVal1 = TestFillValue<AtomicType>();
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
base::subtle::NoBarrier_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
base::subtle::NoBarrier_Store(&value, kVal2);
EXPECT_EQ(kVal2, value);
base::subtle::Acquire_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
base::subtle::Acquire_Store(&value, kVal2);
EXPECT_EQ(kVal2, value);
base::subtle::Release_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
base::subtle::Release_Store(&value, kVal2);
EXPECT_EQ(kVal2, value);
}
// This is a simple sanity check that values are correct. Not testing
// atomicity
template <class AtomicType>
static void TestLoad() {
const AtomicType kVal1 = TestFillValue<AtomicType>();
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
value = kVal1;
EXPECT_EQ(kVal1, base::subtle::NoBarrier_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, base::subtle::NoBarrier_Load(&value));
value = kVal1;
EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value));
value = kVal1;
EXPECT_EQ(kVal1, base::subtle::Release_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, base::subtle::Release_Load(&value));
}
TEST(AtomicOpsTest, Inc) {
TestAtomicIncrement<base::subtle::Atomic32>();
TestAtomicIncrement<base::subtle::AtomicWord>();
}
TEST(AtomicOpsTest, CompareAndSwap) {
TestCompareAndSwap<base::subtle::Atomic32>();
TestCompareAndSwap<base::subtle::AtomicWord>();
}
TEST(AtomicOpsTest, Exchange) {
TestAtomicExchange<base::subtle::Atomic32>();
TestAtomicExchange<base::subtle::AtomicWord>();
}
TEST(AtomicOpsTest, IncrementBounds) {
TestAtomicIncrementBounds<base::subtle::Atomic32>();
TestAtomicIncrementBounds<base::subtle::AtomicWord>();
}
TEST(AtomicOpsTest, Store) {
TestStore<base::subtle::Atomic32>();
TestStore<base::subtle::AtomicWord>();
}
TEST(AtomicOpsTest, Load) {
TestLoad<base::subtle::Atomic32>();
TestLoad<base::subtle::AtomicWord>();
}

Просмотреть файл

@ -0,0 +1,724 @@
# Copyright (c) 2009 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'chromium_code': 1,
},
'includes': [
'../build/common.gypi',
],
'targets': [
{
'target_name': 'base',
'type': '<(library)',
'dependencies': [
'../third_party/icu38/icu38.gyp:icui18n',
'../third_party/icu38/icu38.gyp:icuuc',
],
'msvs_guid': '1832A374-8A74-4F9E-B536-69A699B3E165',
'sources': [
'../build/build_config.h',
'crypto/cssm_init.cc',
'crypto/cssm_init.h',
'crypto/signature_verifier.h',
'crypto/signature_verifier_mac.cc',
'crypto/signature_verifier_nss.cc',
'crypto/signature_verifier_win.cc',
'third_party/dmg_fp/dmg_fp.h',
'third_party/dmg_fp/dtoa.cc',
'third_party/dmg_fp/g_fmt.cc',
'third_party/nspr/prcpucfg.h',
'third_party/nspr/prcpucfg_win.h',
'third_party/nspr/prtime.cc',
'third_party/nspr/prtime.h',
'third_party/nspr/prtypes.h',
'third_party/nss/blapi.h',
'third_party/nss/blapit.h',
'third_party/nss/sha256.h',
'third_party/nss/sha512.cc',
'third_party/purify/pure.h',
'third_party/purify/pure_api.c',
'atomicops_internals_x86_gcc.cc',
'at_exit.cc',
'at_exit.h',
'atomic_ref_count.h',
'atomic_sequence_num.h',
'atomicops.h',
'atomicops_internals_x86_msvc.h',
'base_drag_source.cc',
'base_drag_source.h',
'base_drop_target.cc',
'base_drop_target.h',
'base_paths.cc',
'base_paths.h',
'base_paths_linux.h',
'base_paths_linux.cc',
'base_paths_mac.h',
'base_paths_mac.mm',
'base_paths_win.cc',
'base_paths_win.h',
'base_switches.cc',
'base_switches.h',
'basictypes.h',
'bzip2_error_handler.cc',
'clipboard.cc',
'clipboard.h',
'clipboard_linux.cc',
'clipboard_mac.mm',
'clipboard_util.cc',
'clipboard_util.h',
'clipboard_win.cc',
'command_line.cc',
'command_line.h',
'compiler_specific.h',
'condition_variable.h',
'condition_variable_posix.cc',
'condition_variable_win.cc',
'cpu.cc',
'cpu.h',
'data_pack.cc',
'debug_on_start.cc',
'debug_on_start.h',
'debug_util.cc',
'debug_util.h',
'debug_util_mac.cc',
'debug_util_posix.cc',
'debug_util_win.cc',
'directory_watcher.h',
'directory_watcher_inotify.cc',
'directory_watcher_mac.cc',
'directory_watcher_win.cc',
'event_recorder.cc',
'event_recorder.h',
'event_recorder_stubs.cc',
'field_trial.cc',
'field_trial.h',
'file_descriptor_shuffle.cc',
'file_descriptor_shuffle.h',
'file_path.cc',
'file_path.h',
'file_util.cc',
'file_util.h',
'file_util_icu.cc',
'file_util_linux.cc',
'file_util_mac.mm',
'file_util_posix.cc',
'file_util_win.cc',
'file_version_info.cc',
'file_version_info.h',
'file_version_info_linux.cc',
'file_version_info_mac.mm',
'fix_wp64.h',
'float_util.h',
'foundation_utils_mac.h',
'hash_tables.h',
'histogram.cc',
'histogram.h',
'hmac.h',
'hmac_mac.cc',
'hmac_nss.cc',
'hmac_win.cc',
'iat_patch.cc',
'iat_patch.h',
'icu_util.cc',
'icu_util.h',
'id_map.h',
'idle_timer.cc',
'idle_timer.h',
'idle_timer_none.cc',
'image_util.cc',
'image_util.h',
'json_reader.cc',
'json_reader.h',
'json_writer.cc',
'json_writer.h',
'keyboard_codes.h',
'keyboard_codes_win.h',
'lazy_instance.cc',
'lazy_instance.h',
'linked_ptr.h',
'linux_util.cc',
'linux_util.h',
'lock.cc',
'lock.h',
'lock_impl.h',
'lock_impl_posix.cc',
'lock_impl_win.cc',
'logging.cc',
'logging.h',
'mac_util.h',
'mac_util.mm',
'md5.cc',
'md5.h',
'memory_debug.cc',
'memory_debug.h',
'message_loop.cc',
'message_loop.h',
'message_pump.h',
'message_pump_default.cc',
'message_pump_default.h',
'message_pump_glib.cc',
'message_pump_glib.h',
'message_pump_libevent.cc',
'message_pump_libevent.h',
'message_pump_mac.h',
'message_pump_mac.mm',
'message_pump_win.cc',
'message_pump_win.h',
'native_library.h',
'native_library_linux.cc',
'native_library_mac.mm',
'native_library_win.cc',
'non_thread_safe.cc',
'non_thread_safe.h',
'nss_init.cc',
'nss_init.h',
'object_watcher.cc',
'object_watcher.h',
'observer_list.h',
'observer_list_threadsafe.h',
'path_service.cc',
'path_service.h',
'pe_image.cc',
'pe_image.h',
'pickle.cc',
'pickle.h',
'platform_file.h',
'platform_file_win.cc',
'platform_file_posix.cc',
'platform_thread.h',
'platform_thread_mac.mm',
'platform_thread_posix.cc',
'platform_thread_win.cc',
'port.h',
'profiler.cc',
'profiler.h',
'process.h',
'process_posix.cc',
'process_util.h',
'process_util_linux.cc',
'process_util_mac.mm',
'process_util_posix.cc',
'process_util_win.cc',
'process_win.cc',
'rand_util.cc',
'rand_util.h',
'rand_util_posix.cc',
'rand_util_win.cc',
'ref_counted.cc',
'ref_counted.h',
'registry.cc',
'registry.h',
'resource_util.cc',
'resource_util.h',
'revocable_store.cc',
'revocable_store.h',
'scoped_bstr_win.cc',
'scoped_bstr_win.h',
'scoped_cftyperef.h',
'scoped_clipboard_writer.cc',
'scoped_clipboard_writer.h',
'scoped_comptr_win.h',
'scoped_handle.h',
'scoped_handle_win.h',
'scoped_nsautorelease_pool.h',
'scoped_nsautorelease_pool.mm',
'scoped_nsobject.h',
'scoped_ptr.h',
'scoped_temp_dir.cc',
'scoped_temp_dir.h',
'scoped_variant_win.cc',
'scoped_variant_win.h',
'scoped_vector.h',
'sha2.cc',
'sha2.h',
'shared_memory.h',
'shared_memory_posix.cc',
'shared_memory_win.cc',
'simple_thread.cc',
'simple_thread.h',
'singleton.h',
'spin_wait.h',
'stack_container.h',
'stats_counters.h',
'stats_table.cc',
'stats_table.h',
'stl_util-inl.h',
'string16.cc',
'string16.h',
'string_escape.cc',
'string_escape.h',
'string_piece.cc',
'string_piece.h',
'string_tokenizer.h',
'string_util.cc',
'string_util.h',
'string_util_icu.cc',
'string_util_win.h',
'sys_info.h',
'sys_info_mac.cc',
'sys_info_posix.cc',
'sys_info_win.cc',
'sys_string_conversions.h',
'sys_string_conversions_linux.cc',
'sys_string_conversions_mac.mm',
'sys_string_conversions_win.cc',
'system_monitor.cc',
'system_monitor.h',
'system_monitor_posix.cc',
'system_monitor_win.cc',
'task.h',
'test_file_util.h',
'test_file_util_linux.cc',
'test_file_util_mac.cc',
'test_file_util_posix.cc',
'test_file_util_win.cc',
'thread.cc',
'thread.h',
'thread_collision_warner.cc',
'thread_collision_warner.h',
'thread_local.h',
'thread_local_posix.cc',
'thread_local_storage.h',
'thread_local_storage_posix.cc',
'thread_local_storage_win.cc',
'thread_local_win.cc',
'time.cc',
'time.h',
'time_format.cc',
'time_format.h',
'time_mac.cc',
'time_posix.cc',
'time_win.cc',
'timer.cc',
'timer.h',
'trace_event.cc',
'trace_event.h',
'tracked.cc',
'tracked.h',
'tracked_objects.cc',
'tracked_objects.h',
'tuple.h',
'values.cc',
'values.h',
'version.cc',
'version.h',
'waitable_event.h',
'waitable_event_posix.cc',
'waitable_event_watcher.h',
'waitable_event_watcher_posix.cc',
'waitable_event_watcher_win.cc',
'waitable_event_win.cc',
'watchdog.cc',
'watchdog.h',
'win_util.cc',
'win_util.h',
'windows_message_list.h',
'wmi_util.cc',
'wmi_util.h',
'word_iterator.cc',
'word_iterator.h',
'worker_pool.h',
'worker_pool_linux.cc',
'worker_pool_linux.h',
'worker_pool_mac.mm',
'worker_pool_win.cc',
],
'include_dirs': [
'..',
],
'direct_dependent_settings': {
'include_dirs': [
'..',
],
},
# These warnings are needed for the files in third_party\dmg_fp.
'msvs_disabled_warnings': [
4244, 4554, 4018, 4102,
],
'conditions': [
[ 'OS == "linux"', {
'actions': [
{
'action_name': 'linux_version',
'variables': {
'template_input_path': 'file_version_info_linux.h.version',
'template_output_path':
'<(SHARED_INTERMEDIATE_DIR)/base/file_version_info_linux.h',
},
'inputs': [
'<(template_input_path)',
'../chrome/VERSION',
'../chrome/tools/build/linux/version.sh',
],
'conditions': [
[ 'branding == "Chrome"', {
'inputs': ['../chrome/app/theme/google_chrome/BRANDING']
}, { # else branding!="Chrome"
'inputs': ['../chrome/app/theme/chromium/BRANDING']
}],
],
'outputs': [
# Use a non-existant output so this action always runs and
# generates version information, e.g. to capture revision
# changes, which aren't captured by file dependencies.
'<(SHARED_INTERMEDIATE_DIR)/base/file_version_info_linux.bogus',
],
'action': [
'../chrome/tools/build/linux/version.sh',
'<(template_input_path)', '<(template_output_path)',
],
},
],
'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)',
],
'sources/': [ ['exclude', '_(mac|win)\\.cc$'],
['exclude', '\\.mm?$' ] ],
'sources!': [
# Linux has an implementation of idle_timer that depends
# on XScreenSaver, but it's unclear if we want it yet,
# so use idle_timer_none.cc instead.
'idle_timer.cc',
],
'dependencies': [
'../build/linux/system.gyp:gtk',
'../build/linux/system.gyp:nss',
],
'cflags': [
'-Wno-write-strings',
],
'link_settings': {
'libraries': [
# We need rt for clock_gettime().
'-lrt',
],
},
},
{ # else: OS != "linux"
'sources!': [
'crypto/signature_verifier_nss.cc',
'atomicops_internals_x86_gcc.cc',
'directory_watcher_inotify.cc',
'hmac_nss.cc',
'idle_timer_none.cc',
'linux_util.cc',
'message_pump_glib.cc',
'nss_init.cc',
'nss_init.h',
'time_posix.cc',
],
}
],
[ 'GENERATOR == "quentin"', {
# Quentin builds don't have a recent enough glibc to include the
# inotify headers
'sources!': [
'directory_watcher_inotify.cc',
],
'sources': [
'directory_watcher_stub.cc',
],
},
],
[ 'OS == "mac"', {
'sources/': [ ['exclude', '_(linux|win)\\.cc$'] ],
'sources!': [
],
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
'$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
'$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
'$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
'$(SDKROOT)/System/Library/Frameworks/Security.framework',
],
},
},
{ # else: OS != "mac"
'sources!': [
'crypto/cssm_init.cc',
'crypto/cssm_init.h',
],
}
],
[ 'OS == "win"', {
'sources/': [ ['exclude', '_(linux|mac|posix)\\.cc$'],
['exclude', '\\.mm?$' ] ],
'sources!': [
'data_pack.cc',
'event_recorder_stubs.cc',
'file_descriptor_shuffle.cc',
'message_pump_libevent.cc',
'string16.cc',
],
},
{ # else: OS != "win"
'dependencies': ['../third_party/libevent/libevent.gyp:libevent'],
'sources!': [
'third_party/purify/pure_api.c',
'base_drag_source.cc',
'base_drop_target.cc',
'cpu.cc',
'clipboard_util.cc',
'debug_on_start.cc',
'event_recorder.cc',
'file_version_info.cc',
'iat_patch.cc',
'image_util.cc',
'object_watcher.cc',
'pe_image.cc',
'registry.cc',
'resource_util.cc',
'win_util.cc',
'wmi_util.cc',
],
},
],
],
},
{
'target_name': 'base_gfx',
'type': '<(library)',
'msvs_guid': 'A508ADD3-CECE-4E0F-8448-2F5E454DF551',
'sources': [
'gfx/gdi_util.cc',
'gfx/gdi_util.h',
'gfx/gtk_native_view_id_manager.cc',
'gfx/gtk_native_view_id_manager.h',
'gfx/gtk_util.cc',
'gfx/gtk_util.h',
'gfx/jpeg_codec.cc',
'gfx/jpeg_codec.h',
'gfx/native_theme.cc',
'gfx/native_theme.h',
'gfx/native_widget_types.h',
'gfx/native_widget_types_gtk.cc',
'gfx/platform_canvas.h',
'gfx/platform_canvas_linux.h',
'gfx/platform_canvas_mac.h',
'gfx/platform_device_linux.h',
'gfx/platform_device_mac.h',
'gfx/png_decoder.cc',
'gfx/png_decoder.h',
'gfx/png_encoder.cc',
'gfx/png_encoder.h',
'gfx/point.cc',
'gfx/point.h',
'gfx/rect.cc',
'gfx/rect.h',
'gfx/size.cc',
'gfx/size.h',
],
'mac_framework_dirs': [
'$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework/Frameworks',
],
'dependencies': [
'base',
'../skia/skia.gyp:skia',
'../third_party/libjpeg/libjpeg.gyp:libjpeg',
'../third_party/libpng/libpng.gyp:libpng',
'../third_party/zlib/zlib.gyp:zlib',
],
'export_dependent_settings': [
'base',
],
'conditions': [
['OS == "linux"', {
'dependencies': [
'../build/linux/system.gyp:gtk',
],
}],
[ 'OS != "win"', { 'sources!': [
'gfx/gdi_util.cc',
'gfx/native_theme.cc',
],
}],
[ 'OS != "linux"', { 'sources!': [
'gfx/gtk_native_view_id_manager.cc',
'gfx/gtk_util.cc',
'gfx/native_widget_types_gtk.cc',
],
}],
],
},
{
'target_name': 'base_unittests',
'type': 'executable',
'msvs_guid': '27A30967-4BBA-48D1-8522-CDE95F7B1CEC',
'sources': [
'crypto/signature_verifier_unittest.cc',
'gfx/jpeg_codec_unittest.cc',
'gfx/native_theme_unittest.cc',
'gfx/png_codec_unittest.cc',
'gfx/rect_unittest.cc',
'at_exit_unittest.cc',
'atomicops_unittest.cc',
'clipboard_unittest.cc',
'command_line_unittest.cc',
'condition_variable_unittest.cc',
'data_pack_unittest.cc',
'debug_util_unittest.cc',
'directory_watcher_unittest.cc',
'field_trial_unittest.cc',
'file_descriptor_shuffle_unittest.cc',
'file_path_unittest.cc',
'file_util_unittest.cc',
'file_version_info_unittest.cc',
'histogram_unittest.cc',
'hmac_unittest.cc',
'idletimer_unittest.cc',
'json_reader_unittest.cc',
'json_writer_unittest.cc',
'lazy_instance_unittest.cc',
'linked_ptr_unittest.cc',
'mac_util_unittest.cc',
'message_loop_unittest.cc',
'object_watcher_unittest.cc',
'observer_list_unittest.cc',
'path_service_unittest.cc',
'pe_image_unittest.cc',
'pickle_unittest.cc',
'pr_time_unittest.cc',
'process_util_unittest.cc',
'rand_util_unittest.cc',
'ref_counted_unittest.cc',
'run_all_unittests.cc',
'scoped_bstr_win_unittest.cc',
'scoped_comptr_win_unittest.cc',
'scoped_ptr_unittest.cc',
'scoped_temp_dir_unittest.cc',
'scoped_variant_win_unittest.cc',
'sha2_unittest.cc',
'shared_memory_unittest.cc',
'simple_thread_unittest.cc',
'singleton_unittest.cc',
'stack_container_unittest.cc',
'stats_table_unittest.cc',
'string_escape_unittest.cc',
'string_piece_unittest.cc',
'string_tokenizer_unittest.cc',
'string_util_unittest.cc',
'sys_info_unittest.cc',
'sys_string_conversions_unittest.cc',
'system_monitor_unittest.cc',
'thread_collision_warner_unittest.cc',
'thread_local_storage_unittest.cc',
'thread_local_unittest.cc',
'thread_unittest.cc',
'time_unittest.cc',
'time_win_unittest.cc',
'timer_unittest.cc',
'tracked_objects_unittest.cc',
'tuple_unittest.cc',
'values_unittest.cc',
'version_unittest.cc',
'waitable_event_unittest.cc',
'waitable_event_watcher_unittest.cc',
'watchdog_unittest.cc',
'win_util_unittest.cc',
'wmi_util_unittest.cc',
'word_iterator_unittest.cc',
'worker_pool_unittest.cc',
],
'include_dirs': [
# word_iterator.h (used by word_iterator_unittest.cc) leaks an ICU
# #include for unicode/uchar.h. This should probably be cleaned up.
'../third_party/icu38/public/common',
],
'dependencies': [
'base',
'base_gfx',
'../skia/skia.gyp:skia',
'../testing/gtest.gyp:gtest',
],
'conditions': [
['OS == "linux"', {
'sources!': [
'file_version_info_unittest.cc',
# Linux has an implementation of idle_timer, but it's unclear
# if we want it yet, so leave it 'unported' for now.
'idletimer_unittest.cc',
'worker_pool_linux_unittest.cc',
],
'dependencies': [
'../build/linux/system.gyp:gtk',
'../build/linux/system.gyp:nss',
],
}],
['OS != "mac"', {
'sources!': [
'mac_util_unittest.cc',
],
}],
# This is needed to trigger the dll copy step on windows.
# TODO(mark): This should not be necessary.
['OS == "win"', {
'dependencies': [
'../third_party/icu38/icu38.gyp:icudata',
],
'sources!': [
'data_pack_unittest.cc',
'file_descriptor_shuffle_unittest.cc',
],
}, { # OS != "win"
'sources!': [
'gfx/native_theme_unittest.cc',
'object_watcher_unittest.cc',
'pe_image_unittest.cc',
'scoped_bstr_win_unittest.cc',
'scoped_comptr_win_unittest.cc',
'scoped_variant_win_unittest.cc',
'system_monitor_unittest.cc',
'time_win_unittest.cc',
'win_util_unittest.cc',
'wmi_util_unittest.cc',
],
}],
],
},
{
'target_name': 'test_support_base',
'type': '<(library)',
'dependencies': [
'base',
'../testing/gtest.gyp:gtest',
],
'sources': [
'perftimer.cc',
'run_all_perftests.cc',
],
'direct_dependent_settings': {
'defines': [
'PERF_TEST',
],
},
'conditions': [
['OS == "linux"', {
'dependencies': [
# Needed to handle the #include chain:
# base/perf_test_suite.h
# base/test_suite.h
# gtk/gtk.h
'../build/linux/system.gyp:gtk',
],
}],
],
},
],
'conditions': [
[ 'OS == "win"', {
'targets': [
{
'target_name': 'debug_message',
'type': 'executable',
'sources': [
'debug_message.cc',
],
},
],
}],
],
}

Просмотреть файл

@ -0,0 +1,60 @@
// Copyright (c) 2006-2008 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/base_drag_source.h"
///////////////////////////////////////////////////////////////////////////////
// BaseDragSource, public:
BaseDragSource::BaseDragSource() : ref_count_(0) {
}
///////////////////////////////////////////////////////////////////////////////
// BaseDragSource, IDropSource implementation:
HRESULT BaseDragSource::QueryContinueDrag(BOOL escape_pressed,
DWORD key_state) {
if (escape_pressed) {
OnDragSourceCancel();
return DRAGDROP_S_CANCEL;
}
if (!(key_state & MK_LBUTTON)) {
OnDragSourceDrop();
return DRAGDROP_S_DROP;
}
OnDragSourceMove();
return S_OK;
}
HRESULT BaseDragSource::GiveFeedback(DWORD effect) {
return DRAGDROP_S_USEDEFAULTCURSORS;
}
///////////////////////////////////////////////////////////////////////////////
// BaseDragSource, IUnknown implementation:
HRESULT BaseDragSource::QueryInterface(const IID& iid, void** object) {
*object = NULL;
if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropSource)) {
*object = this;
} else {
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG BaseDragSource::AddRef() {
return ++ref_count_;
}
ULONG BaseDragSource::Release() {
if (--ref_count_ == 0) {
delete this;
return 0U;
}
return ref_count_;
}

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) 2006-2008 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_BASE_DRAG_SOURCE_H_
#define BASE_BASE_DRAG_SOURCE_H_
#include <objidl.h>
#include "base/basictypes.h"
///////////////////////////////////////////////////////////////////////////////
//
// BaseDragSource
//
// A base IDropSource implementation. Handles notifications sent by an active
// drag-drop operation as the user mouses over other drop targets on their
// system. This object tells Windows whether or not the drag should continue,
// and supplies the appropriate cursors.
//
class BaseDragSource : public IDropSource {
public:
BaseDragSource();
virtual ~BaseDragSource() { }
// IDropSource implementation:
HRESULT __stdcall QueryContinueDrag(BOOL escape_pressed, DWORD key_state);
HRESULT __stdcall GiveFeedback(DWORD effect);
// IUnknown implementation:
HRESULT __stdcall QueryInterface(const IID& iid, void** object);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
protected:
virtual void OnDragSourceCancel() { }
virtual void OnDragSourceDrop() { }
virtual void OnDragSourceMove() { }
private:
LONG ref_count_;
DISALLOW_EVIL_CONSTRUCTORS(BaseDragSource);
};
#endif // BASE_BASE_DRAG_SOURCE_H_

Просмотреть файл

@ -0,0 +1,167 @@
// Copyright (c) 2006-2008 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/base_drop_target.h"
#include <shlobj.h>
#include "base/logging.h"
///////////////////////////////////////////////////////////////////////////////
IDropTargetHelper* BaseDropTarget::cached_drop_target_helper_ = NULL;
int32 BaseDropTarget::drag_identity_ = 0;
BaseDropTarget::BaseDropTarget(HWND hwnd)
: hwnd_(hwnd),
suspend_(false),
ref_count_(0) {
DCHECK(hwnd);
HRESULT result = RegisterDragDrop(hwnd, this);
DCHECK(SUCCEEDED(result));
}
BaseDropTarget::~BaseDropTarget() {
}
// static
IDropTargetHelper* BaseDropTarget::DropHelper() {
if (!cached_drop_target_helper_) {
CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER,
IID_IDropTargetHelper,
reinterpret_cast<void**>(&cached_drop_target_helper_));
}
return cached_drop_target_helper_;
}
///////////////////////////////////////////////////////////////////////////////
// BaseDropTarget, IDropTarget implementation:
HRESULT BaseDropTarget::DragEnter(IDataObject* data_object,
DWORD key_state,
POINTL cursor_position,
DWORD* effect) {
// Tell the helper that we entered so it can update the drag image.
IDropTargetHelper* drop_helper = DropHelper();
if (drop_helper) {
drop_helper->DragEnter(GetHWND(), data_object,
reinterpret_cast<POINT*>(&cursor_position), *effect);
}
// You can't drag and drop within the same HWND.
if (suspend_) {
*effect = DROPEFFECT_NONE;
return S_OK;
}
// Update the drag identity, skipping 0.
if (++drag_identity_ == 0)
++drag_identity_;
current_data_object_ = data_object;
POINT screen_pt = { cursor_position.x, cursor_position.y };
*effect = OnDragEnter(current_data_object_, key_state, screen_pt, *effect);
return S_OK;
}
HRESULT BaseDropTarget::DragOver(DWORD key_state,
POINTL cursor_position,
DWORD* effect) {
// Tell the helper that we moved over it so it can update the drag image.
IDropTargetHelper* drop_helper = DropHelper();
if (drop_helper)
drop_helper->DragOver(reinterpret_cast<POINT*>(&cursor_position), *effect);
if (suspend_) {
*effect = DROPEFFECT_NONE;
return S_OK;
}
POINT screen_pt = { cursor_position.x, cursor_position.y };
*effect = OnDragOver(current_data_object_, key_state, screen_pt, *effect);
return S_OK;
}
HRESULT BaseDropTarget::DragLeave() {
// Tell the helper that we moved out of it so it can update the drag image.
IDropTargetHelper* drop_helper = DropHelper();
if (drop_helper)
drop_helper->DragLeave();
OnDragLeave(current_data_object_);
current_data_object_ = NULL;
return S_OK;
}
HRESULT BaseDropTarget::Drop(IDataObject* data_object,
DWORD key_state,
POINTL cursor_position,
DWORD* effect) {
// Tell the helper that we dropped onto it so it can update the drag image.
IDropTargetHelper* drop_helper = DropHelper();
if (drop_helper) {
drop_helper->Drop(current_data_object_,
reinterpret_cast<POINT*>(&cursor_position), *effect);
}
if (suspend_) {
*effect = DROPEFFECT_NONE;
return S_OK;
}
POINT screen_pt = { cursor_position.x, cursor_position.y };
*effect = OnDrop(current_data_object_, key_state, screen_pt, *effect);
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
// BaseDropTarget, IUnknown implementation:
HRESULT BaseDropTarget::QueryInterface(const IID& iid, void** object) {
*object = NULL;
if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropTarget)) {
*object = this;
} else {
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG BaseDropTarget::AddRef() {
return ++ref_count_;
}
ULONG BaseDropTarget::Release() {
if (--ref_count_ == 0) {
delete this;
return 0U;
}
return ref_count_;
}
DWORD BaseDropTarget::OnDragEnter(IDataObject* data_object,
DWORD key_state,
POINT cursor_position,
DWORD effect) {
return DROPEFFECT_NONE;
}
DWORD BaseDropTarget::OnDragOver(IDataObject* data_object,
DWORD key_state,
POINT cursor_position,
DWORD effect) {
return DROPEFFECT_NONE;
}
void BaseDropTarget::OnDragLeave(IDataObject* data_object) {
}
DWORD BaseDropTarget::OnDrop(IDataObject* data_object,
DWORD key_state,
POINT cursor_position,
DWORD effect) {
return DROPEFFECT_NONE;
}

Просмотреть файл

@ -0,0 +1,130 @@
// Copyright (c) 2006-2008 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_BASE_DROP_TARGET_H_
#define BASE_BASE_DROP_TARGET_H_
#include <objidl.h>
#include "base/ref_counted.h"
struct IDropTargetHelper;
// A DropTarget implementation that takes care of the nitty gritty
// of dnd. While this class is concrete, subclasses will most likely
// want to override various OnXXX methods.
//
// Because BaseDropTarget is ref counted you shouldn't delete it directly,
// rather wrap it in a scoped_refptr. Be sure and invoke RevokeDragDrop(m_hWnd)
// before the HWND is deleted too.
//
// This class is meant to be used in a STA and is not multithread-safe.
class BaseDropTarget : public IDropTarget {
public:
// Create a new BaseDropTarget associating it with the given HWND.
explicit BaseDropTarget(HWND hwnd);
virtual ~BaseDropTarget();
// When suspend is set to |true|, the drop target does not receive drops from
// drags initiated within the owning HWND.
// TODO(beng): (http://b/1085385) figure out how we will handle legitimate
// drag-drop operations within the same HWND, such as dragging
// selected text to an edit field.
void set_suspend(bool suspend) { suspend_ = suspend; }
// IDropTarget implementation:
HRESULT __stdcall DragEnter(IDataObject* data_object,
DWORD key_state,
POINTL cursor_position,
DWORD* effect);
HRESULT __stdcall DragOver(DWORD key_state,
POINTL cursor_position,
DWORD* effect);
HRESULT __stdcall DragLeave();
HRESULT __stdcall Drop(IDataObject* data_object,
DWORD key_state,
POINTL cursor_position,
DWORD* effect);
// IUnknown implementation:
HRESULT __stdcall QueryInterface(const IID& iid, void** object);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
protected:
// Returns the hosting HWND.
HWND GetHWND() { return hwnd_; }
// Invoked when the cursor first moves over the hwnd during a dnd session.
// This should return a bitmask of the supported drop operations:
// DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or
// DROPEFFECT_MOVE.
virtual DWORD OnDragEnter(IDataObject* data_object,
DWORD key_state,
POINT cursor_position,
DWORD effect);
// Invoked when the cursor moves over the window during a dnd session.
// This should return a bitmask of the supported drop operations:
// DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or
// DROPEFFECT_MOVE.
virtual DWORD OnDragOver(IDataObject* data_object,
DWORD key_state,
POINT cursor_position,
DWORD effect);
// Invoked when the cursor moves outside the bounds of the hwnd during a
// dnd session.
virtual void OnDragLeave(IDataObject* data_object);
// Invoked when the drop ends on the window. This should return the operation
// that was taken.
virtual DWORD OnDrop(IDataObject* data_object,
DWORD key_state,
POINT cursor_position,
DWORD effect);
// Return the drag identity.
static int32 GetDragIdentity() { return drag_identity_; }
private:
// Returns the cached drop helper, creating one if necessary. The returned
// object is not addrefed. May return NULL if the object couldn't be created.
static IDropTargetHelper* DropHelper();
// The data object currently being dragged over this drop target.
scoped_refptr<IDataObject> current_data_object_;
// A helper object that is used to provide drag image support while the mouse
// is dragging over the content area.
//
// DO NOT ACCESS DIRECTLY! Use DropHelper() instead, which will lazily create
// this if it doesn't exist yet. This object can take tens of milliseconds to
// create, and we don't want to block any window opening for this, especially
// since often, DnD will never be used. Instead, we force this penalty to the
// first time it is actually used.
static IDropTargetHelper* cached_drop_target_helper_;
// The drag identity (id). An up-counter that increases when the cursor first
// moves over the HWND in a DnD session (OnDragEnter). 0 is reserved to mean
// the "no/unknown" identity, and is used for initialization. The identity is
// sent to the renderer in drag enter notifications. Note: the identity value
// is passed over the renderer NPAPI interface to gears, so use int32 instead
// of int here.
static int32 drag_identity_;
// The HWND of the source. This HWND is used to determine coordinates for
// mouse events that are sent to the renderer notifying various drag states.
HWND hwnd_;
// Whether or not we are currently processing drag notifications for drags
// initiated in this window.
bool suspend_;
LONG ref_count_;
DISALLOW_EVIL_CONSTRUCTORS(BaseDropTarget);
};
#endif // BASE_BASE_DROP_TARGET_H_

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) 2006-2008 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/base_paths.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
namespace base {
bool PathProvider(int key, FilePath* result) {
// NOTE: DIR_CURRENT is a special cased in PathService::Get
FilePath cur;
switch (key) {
case base::DIR_EXE:
PathService::Get(base::FILE_EXE, &cur);
cur = cur.DirName();
break;
case base::DIR_MODULE:
PathService::Get(base::FILE_MODULE, &cur);
cur = cur.DirName();
break;
case base::DIR_TEMP:
if (!file_util::GetTempDir(&cur))
return false;
break;
default:
return false;
}
*result = cur;
return true;
}
} // namespace base

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) 2006-2008 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_BASE_PATHS_H_
#define BASE_BASE_PATHS_H_
// This file declares path keys for the base module. These can be used with
// the PathService to access various special directories and files.
#include "base/basictypes.h"
#if defined(OS_WIN)
#include "base/base_paths_win.h"
#elif defined(OS_MACOSX)
#include "base/base_paths_mac.h"
#elif defined(OS_LINUX)
#include "base/base_paths_linux.h"
#endif
#include "base/path_service.h"
namespace base {
enum {
PATH_START = 0,
DIR_CURRENT, // current directory
DIR_EXE, // directory containing FILE_EXE
DIR_MODULE, // directory containing FILE_MODULE
DIR_TEMP, // temporary directory
PATH_END
};
} // namespace base
#endif // BASE_BASE_PATHS_H_

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) 2006-2008 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/base_paths_linux.h"
#include <unistd.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_piece.h"
#include "base/sys_string_conversions.h"
namespace base {
bool PathProviderLinux(int key, FilePath* result) {
FilePath path;
switch (key) {
case base::FILE_EXE:
case base::FILE_MODULE: { // TODO(evanm): is this correct?
char bin_dir[PATH_MAX + 1];
int bin_dir_size = readlink("/proc/self/exe", bin_dir, PATH_MAX);
if (bin_dir_size < 0 || bin_dir_size > PATH_MAX) {
NOTREACHED() << "Unable to resolve /proc/self/exe.";
return false;
}
bin_dir[bin_dir_size] = 0;
*result = FilePath(bin_dir);
return true;
}
case base::DIR_SOURCE_ROOT:
// On linux, unit tests execute two levels deep from the source root.
// For example: sconsbuild/{Debug|Release}/net_unittest
if (!PathService::Get(base::DIR_EXE, &path))
return false;
path = path.Append(FilePath::kParentDirectory)
.Append(FilePath::kParentDirectory);
*result = path;
return true;
}
return false;
}
} // namespace base

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) 2006-2008 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_BASE_PATHS_LINUX_H_
#define BASE_BASE_PATHS_LINUX_H_
// This file declares Linux-specific path keys for the base module.
// These can be used with the PathService to access various special
// directories and files.
namespace base {
enum {
PATH_LINUX_START = 200,
FILE_EXE, // Path and filename of the current executable.
FILE_MODULE, // Path and filename of the module containing the code for the
// PathService (which could differ from FILE_EXE if the
// PathService were compiled into a shared object, for example).
DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful
// for tests that need to locate various resources. It
// should not be used outside of test code.
PATH_LINUX_END
};
} // namespace base
#endif // BASE_BASE_PATHS_LINUX_H_

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) 2006-2008 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_BASE_PATHS_MAC_H_
#define BASE_BASE_PATHS_MAC_H_
// This file declares Mac-specific path keys for the base module.
// These can be used with the PathService to access various special
// directories and files.
namespace base {
enum {
PATH_MAC_START = 200,
FILE_EXE, // path and filename of the current executable
FILE_MODULE, // path and filename of the module containing the code for the
// PathService (which could differ from FILE_EXE if the
// PathService were compiled into a library, for example)
DIR_APP_DATA, // ~/Library/Application Support/Google/Chrome
DIR_LOCAL_APP_DATA, // same as above (can we remove?)
DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful
// for tests that need to locate various resources. It
// should not be used outside of test code.
PATH_MAC_END
};
} // namespace base
#endif // BASE_BASE_PATHS_MAC_H_

Просмотреть файл

@ -0,0 +1,58 @@
// Copyright (c) 2006-2008 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/base_paths_mac.h"
#import <Cocoa/Cocoa.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/mac_util.h"
#include "base/path_service.h"
#include "base/string_util.h"
namespace base {
bool PathProviderMac(int key, FilePath* result) {
std::string cur;
switch (key) {
case base::FILE_EXE:
case base::FILE_MODULE: {
// Executable path can have relative references ("..") depending on
// how the app was launched.
NSString* path =
[[[NSBundle mainBundle] executablePath] stringByStandardizingPath];
cur = [path fileSystemRepresentation];
break;
}
case base::DIR_SOURCE_ROOT: {
FilePath path;
PathService::Get(base::DIR_EXE, &path);
if (mac_util::AmIBundled()) {
// The bundled app executables (Chromium, TestShell, etc) live five
// levels down, eg:
// src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium.
path = path.DirName();
path = path.DirName();
path = path.DirName();
path = path.DirName();
*result = path.DirName();
} else {
// Unit tests execute two levels deep from the source root, eg:
// src/xcodebuild/{Debug|Release}/base_unittests
path = path.DirName();
*result = path.DirName();
}
return true;
}
default:
return false;
}
*result = FilePath(cur);
return true;
}
} // namespace base

Просмотреть файл

@ -0,0 +1,118 @@
// Copyright (c) 2006-2008 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/base_paths_win.h"
#include <windows.h>
#include <shlobj.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/win_util.h"
// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace base {
bool PathProviderWin(int key, FilePath* result) {
// We need to go compute the value. It would be nice to support paths with
// names longer than MAX_PATH, but the system functions don't seem to be
// designed for it either, with the exception of GetTempPath (but other
// things will surely break if the temp path is too long, so we don't bother
// handling it.
wchar_t system_buffer[MAX_PATH];
system_buffer[0] = 0;
FilePath cur;
std::wstring wstring_path;
switch (key) {
case base::FILE_EXE:
GetModuleFileName(NULL, system_buffer, MAX_PATH);
cur = FilePath(system_buffer);
break;
case base::FILE_MODULE: {
// the resource containing module is assumed to be the one that
// this code lives in, whether that's a dll or exe
HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
GetModuleFileName(this_module, system_buffer, MAX_PATH);
cur = FilePath(system_buffer);
break;
}
case base::DIR_WINDOWS:
GetWindowsDirectory(system_buffer, MAX_PATH);
cur = FilePath(system_buffer);
break;
case base::DIR_SYSTEM:
GetSystemDirectory(system_buffer, MAX_PATH);
cur = FilePath(system_buffer);
break;
case base::DIR_PROGRAM_FILES:
if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
SHGFP_TYPE_CURRENT, system_buffer)))
return false;
cur = FilePath(system_buffer);
break;
case base::DIR_IE_INTERNET_CACHE:
if (FAILED(SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL,
SHGFP_TYPE_CURRENT, system_buffer)))
return false;
cur = FilePath(system_buffer);
break;
case base::DIR_COMMON_START_MENU:
if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL,
SHGFP_TYPE_CURRENT, system_buffer)))
return false;
cur = FilePath(system_buffer);
break;
case base::DIR_START_MENU:
if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAMS, NULL,
SHGFP_TYPE_CURRENT, system_buffer)))
return false;
cur = FilePath(system_buffer);
break;
case base::DIR_APP_DATA:
if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
system_buffer)))
return false;
cur = FilePath(system_buffer);
break;
case base::DIR_LOCAL_APP_DATA_LOW:
if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) {
return false;
}
// TODO(nsylvain): We should use SHGetKnownFolderPath instead. Bug 1281128
if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
system_buffer)))
return false;
wstring_path = system_buffer;
file_util::UpOneDirectory(&wstring_path);
file_util::AppendToPath(&wstring_path, L"LocalLow");
cur = FilePath(wstring_path);
break;
case base::DIR_LOCAL_APP_DATA:
if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL,
SHGFP_TYPE_CURRENT, system_buffer)))
return false;
cur = FilePath(system_buffer);
break;
case base::DIR_SOURCE_ROOT:
// On Windows, unit tests execute two levels deep from the source root.
// For example: chrome/{Debug|Release}/ui_tests.exe
PathService::Get(base::DIR_EXE, &wstring_path);
file_util::UpOneDirectory(&wstring_path);
file_util::UpOneDirectory(&wstring_path);
cur = FilePath(wstring_path);
break;
default:
return false;
}
*result = cur;
return true;
}
} // namespace base

Просмотреть файл

@ -0,0 +1,42 @@
// Copyright (c) 2006-2008 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_BASE_PATHS_WIN_H__
#define BASE_BASE_PATHS_WIN_H__
// This file declares windows-specific path keys for the base module.
// These can be used with the PathService to access various special
// directories and files.
namespace base {
enum {
PATH_WIN_START = 100,
FILE_EXE, // path and filename of the current executable
FILE_MODULE, // path and filename of the module containing the code for the
// PathService (which could differ from FILE_EXE if the
// PathService were compiled into a DLL, for example)
DIR_WINDOWS, // Windows directory, usually "c:\windows"
DIR_SYSTEM, // Usually c:\windows\system32"
DIR_PROGRAM_FILES, // Usually c:\program files
DIR_IE_INTERNET_CACHE, // Temporary Internet Files directory.
DIR_COMMON_START_MENU, // Usually "C:\Documents and Settings\All Users\
// Start Menu\Programs"
DIR_START_MENU, // Usually "C:\Documents and Settings\<user>\
// Start Menu\Programs"
DIR_APP_DATA, // Application Data directory under the user profile.
DIR_LOCAL_APP_DATA_LOW, // Local AppData directory for low integrity level.
DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory under the
// user profile.
DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful
// for tests that need to locate various resources. It
// should not be used outside of test code.
PATH_WIN_END
};
} // namespace base
#endif // BASE_BASE_PATHS_WIN_H__

Просмотреть файл

@ -0,0 +1,43 @@
// Copyright (c) 2006-2008 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/base_switches.h"
namespace switches {
// If the program includes chrome/common/debug_on_start.h, the process will
// start the JIT system-registered debugger on itself and will wait for 60
// seconds for the debugger to attach to itself. Then a break point will be hit.
const wchar_t kDebugOnStart[] = L"debug-on-start";
// Will wait for 60 seconds for a debugger to come to attach to the process.
const wchar_t kWaitForDebugger[] = L"wait-for-debugger";
// Suppresses all error dialogs when present.
const wchar_t kNoErrorDialogs[] = L"noerrdialogs";
// Disables the crash reporting.
const wchar_t kDisableBreakpad[] = L"disable-breakpad";
// Generates full memory crash dump.
const wchar_t kFullMemoryCrashReport[] = L"full-memory-crash-report";
// The value of this switch determines whether the process is started as a
// renderer or plugin host. If it's empty, it's the browser.
const wchar_t kProcessType[] = L"type";
// Enable DCHECKs in release mode.
const wchar_t kEnableDCHECK[] = L"enable-dcheck";
// Refuse to make HTTP connections and refuse to accept certificate errors.
// For more information about the design of this feature, please see
//
// ForceHTTPS: Protecting High-Security Web Sites from Network Attacks
// Collin Jackson and Adam Barth
// In Proc. of the 17th International World Wide Web Conference (WWW 2008)
//
// Available at http://www.adambarth.com/papers/2008/jackson-barth.pdf
const wchar_t kForceHTTPS[] = L"force-https";
} // namespace switches

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) 2006-2008 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.
// Defines all the "base" command-line switches.
#ifndef BASE_BASE_SWITCHES_H_
#define BASE_BASE_SWITCHES_H_
namespace switches {
extern const wchar_t kDebugOnStart[];
extern const wchar_t kWaitForDebugger[];
extern const wchar_t kDisableBreakpad[];
extern const wchar_t kFullMemoryCrashReport[];
extern const wchar_t kNoErrorDialogs[];
extern const wchar_t kProcessType[];
extern const wchar_t kEnableDCHECK[];
extern const wchar_t kForceHTTPS[];
} // namespace switches
#endif // BASE_BASE_SWITCHES_H_

Просмотреть файл

@ -0,0 +1,335 @@
// Copyright (c) 2006-2008 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_BASICTYPES_H_
#define BASE_BASICTYPES_H_
#include <limits.h> // So we can set the bounds of our types
#include <stddef.h> // For size_t
#include <string.h> // for memcpy
#include "base/port.h" // Types that only need exist on certain systems
#ifndef COMPILER_MSVC
// stdint.h is part of C99 but MSVC doesn't have it.
#include <stdint.h> // For intptr_t.
#endif
typedef signed char schar;
typedef signed char int8;
typedef short int16;
// TODO(mbelshe) Remove these type guards. These are
// temporary to avoid conflicts with npapi.h.
#ifndef _INT32
#define _INT32
typedef int int32;
#endif
typedef long long int64;
// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
// places. Use the signed types unless your variable represents a bit
// pattern (eg a hash value) or you really need the extra bit. Do NOT
// use 'unsigned' to express "this value should always be positive";
// use assertions for this.
typedef unsigned char uint8;
typedef unsigned short uint16;
// TODO(mbelshe) Remove these type guards. These are
// temporary to avoid conflicts with npapi.h.
#ifndef _UINT32
#define _UINT32
typedef unsigned int uint32;
#endif
typedef unsigned long long uint64;
// A type to represent a Unicode code-point value. As of Unicode 4.0,
// such values require up to 21 bits.
// (For type-checking on pointers, make this explicitly signed,
// and it should always be the signed version of whatever int32 is.)
typedef signed int char32;
const uint8 kuint8max = (( uint8) 0xFF);
const uint16 kuint16max = ((uint16) 0xFFFF);
const uint32 kuint32max = ((uint32) 0xFFFFFFFF);
const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF));
const int8 kint8min = (( int8) 0x80);
const int8 kint8max = (( int8) 0x7F);
const int16 kint16min = (( int16) 0x8000);
const int16 kint16max = (( int16) 0x7FFF);
const int32 kint32min = (( int32) 0x80000000);
const int32 kint32max = (( int32) 0x7FFFFFFF);
const int64 kint64min = (( int64) GG_LONGLONG(0x8000000000000000));
const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF));
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// An older, deprecated, politically incorrect name for the above.
#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
// A macro to disallow all the implicit constructors, namely the
// default constructor, copy constructor and operator= functions.
//
// This should be used in the private: declarations for a class
// that wants to prevent anyone from instantiating it. This is
// especially useful for classes containing only static methods.
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
TypeName(); \
DISALLOW_COPY_AND_ASSIGN(TypeName)
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// One caveat is that arraysize() doesn't accept any array of an
// anonymous type or a type defined inside a function. In these rare
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
// due to a limitation in C++'s template system. The limitation might
// eventually be removed, but it hasn't happened yet.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef _MSC_VER
template <typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
// but can be used on anonymous types or types defined inside
// functions. It's less safe than arraysize as it accepts some
// (although not all) pointers. Therefore, you should use arraysize
// whenever possible.
//
// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
// size_t.
//
// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
//
// "warning: division by zero in ..."
//
// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
//
// The following comments are on the implementation details, and can
// be ignored by the users.
//
// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
// the array) and sizeof(*(arr)) (the # of bytes in one array
// element). If the former is divisible by the latter, perhaps arr is
// indeed an array, in which case the division result is the # of
// elements in the array. Otherwise, arr cannot possibly be an array,
// and we generate a compiler error to prevent the code from
// compiling.
//
// Since the size of bool is implementation-defined, we need to cast
// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
// result has type size_t.
//
// This macro is not perfect as it wrongfully accepts certain
// pointers, namely where the pointer size is divisible by the pointee
// size. Since all our code has to go through a 32-bit compiler,
// where a pointer is 4 bytes, this means all pointers to a type whose
// size is 3 or greater than 4 will be (righteously) rejected.
#define ARRAYSIZE_UNSAFE(a) \
((sizeof(a) / sizeof(*(a))) / \
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
// Use implicit_cast as a safe version of static_cast or const_cast
// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
// a const pointer to Foo).
// When you use implicit_cast, the compiler checks that the cast is safe.
// Such explicit implicit_casts are necessary in surprisingly many
// situations where C++ demands an exact type match instead of an
// argument type convertable to a target type.
//
// The From type can be inferred, so the preferred syntax for using
// implicit_cast is the same as for static_cast etc.:
//
// implicit_cast<ToType>(expr)
//
// implicit_cast would have been part of the C++ standard library,
// but the proposal was submitted too late. It will probably make
// its way into the language in the future.
template<typename To, typename From>
inline To implicit_cast(From const &f) {
return f;
}
// The COMPILE_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
template <bool>
struct CompileAssert {
};
#undef COMPILE_ASSERT
#define COMPILE_ASSERT(expr, msg) \
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
// Implementation details of COMPILE_ASSERT:
//
// - COMPILE_ASSERT works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
// - The simpler definition
//
// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
//
// does not work, as gcc supports variable-length arrays whose sizes
// are determined at run-time (this is gcc's extension and not part
// of the C++ standard). As a result, gcc fails to reject the
// following code with the simple definition:
//
// int foo;
// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
// // not a compile-time constant.
//
// - By using the type CompileAssert<(bool(expr))>, we ensures that
// expr is a compile-time constant. (Template arguments must be
// determined at compile-time.)
//
// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
//
// CompileAssert<bool(expr)>
//
// instead, these compilers will refuse to compile
//
// COMPILE_ASSERT(5 > 0, some_message);
//
// (They seem to think the ">" in "5 > 0" marks the end of the
// template argument list.)
//
// - The array size is (bool(expr) ? 1 : -1), instead of simply
//
// ((expr) ? 1 : -1).
//
// This is to avoid running into a bug in MS VC 7.1, which
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
// MetatagId refers to metatag-id that we assign to
// each metatag <name, value> pair..
typedef uint32 MetatagId;
// Argument type used in interfaces that can optionally take ownership
// of a passed in argument. If TAKE_OWNERSHIP is passed, the called
// object takes ownership of the argument. Otherwise it does not.
enum Ownership {
DO_NOT_TAKE_OWNERSHIP,
TAKE_OWNERSHIP
};
// bit_cast<Dest,Source> is a template function that implements the
// equivalent of "*reinterpret_cast<Dest*>(&source)". We need this in
// very low-level functions like the protobuf library and fast math
// support.
//
// float f = 3.14159265358979;
// int i = bit_cast<int32>(f);
// // i = 0x40490fdb
//
// The classical address-casting method is:
//
// // WRONG
// float f = 3.14159265358979; // WRONG
// int i = * reinterpret_cast<int*>(&f); // WRONG
//
// The address-casting method actually produces undefined behavior
// according to ISO C++ specification section 3.10 -15 -. Roughly, this
// section says: if an object in memory has one type, and a program
// accesses it with a different type, then the result is undefined
// behavior for most values of "different type".
//
// This is true for any cast syntax, either *(int*)&f or
// *reinterpret_cast<int*>(&f). And it is particularly true for
// conversions betweeen integral lvalues and floating-point lvalues.
//
// The purpose of 3.10 -15- is to allow optimizing compilers to assume
// that expressions with different types refer to different memory. gcc
// 4.0.1 has an optimizer that takes advantage of this. So a
// non-conforming program quietly produces wildly incorrect output.
//
// The problem is not the use of reinterpret_cast. The problem is type
// punning: holding an object in memory of one type and reading its bits
// back using a different type.
//
// The C++ standard is more subtle and complex than this, but that
// is the basic idea.
//
// Anyways ...
//
// bit_cast<> calls memcpy() which is blessed by the standard,
// especially by the example in section 3.9 . Also, of course,
// bit_cast<> wraps up the nasty logic in one place.
//
// Fortunately memcpy() is very fast. In optimized mode, with a
// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
// code with the minimal amount of data movement. On a 32-bit system,
// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
// compiles to two loads and two stores.
//
// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1.
//
// WARNING: if Dest or Source is a non-POD type, the result of the memcpy
// is likely to surprise you.
template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
// Compile time assertion: sizeof(Dest) == sizeof(Source)
// A compile error here means your Dest and Source have different sizes.
typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1];
Dest dest;
memcpy(&dest, &source, sizeof(dest));
return dest;
}
// The following enum should be used only as a constructor argument to indicate
// that the variable has static storage class, and that the constructor should
// do nothing to its state. It indicates to the reader that it is legal to
// declare a static instance of the class, provided the constructor is given
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
// static variable that has a constructor or a destructor because invocation
// order is undefined. However, IF the type can be initialized by filling with
// zeroes (which the loader does for static variables), AND the destructor also
// does nothing to the storage, AND there are no virtual methods, then a
// constructor declared as
// explicit MyClass(base::LinkerInitialized x) {}
// and invoked as
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
namespace base {
enum LinkerInitialized { LINKER_INITIALIZED };
} // base
#endif // BASE_BASICTYPES_H_

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioPropertySheet
ProjectType="Visual C++"
Version="8.00"
Name="base"
InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
>
</VisualStudioPropertySheet>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioPropertySheet
ProjectType="Visual C++"
Version="8.00"
Name="base_gfx"
InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\third_party\libjpeg\using_libjpeg.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;..\..\skia\using_skia.vsprops"
>
</VisualStudioPropertySheet>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioPropertySheet
ProjectType="Visual C++"
Version="8.00"
Name="base_unittests"
InheritedPropertySheets=".\base_gfx.vsprops"
>
</VisualStudioPropertySheet>

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) 2006-2008 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/logging.h"
// We define BZ_NO_STDIO in third_party/bzip2 to remove its internal STDERR
// error reporting. This requires us to export our own error handler.
extern "C"
void bz_internal_error(int errcode) {
CHECK(false) << "bzip2 internal error: " << errcode;
}

Просмотреть файл

@ -0,0 +1,64 @@
// Copyright (c) 2006-2008 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/clipboard.h"
#include "base/logging.h"
namespace {
// A compromised renderer could send us bad data, so validate it.
bool IsBitmapSafe(const Clipboard::ObjectMapParams& params) {
const gfx::Size* size =
reinterpret_cast<const gfx::Size*>(&(params[1].front()));
return params[0].size() ==
static_cast<size_t>(size->width() * size->height() * 4);
}
}
void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
switch (type) {
case CBF_TEXT:
WriteText(&(params[0].front()), params[0].size());
break;
case CBF_HTML:
if (params.size() == 2)
WriteHTML(&(params[0].front()), params[0].size(),
&(params[1].front()), params[1].size());
else
WriteHTML(&(params[0].front()), params[0].size(), NULL, 0);
break;
case CBF_BOOKMARK:
WriteBookmark(&(params[0].front()), params[0].size(),
&(params[1].front()), params[1].size());
break;
case CBF_LINK:
WriteHyperlink(&(params[0].front()), params[0].size(),
&(params[1].front()), params[1].size());
break;
case CBF_FILES:
WriteFiles(&(params[0].front()), params[0].size());
break;
case CBF_WEBKIT:
WriteWebSmartPaste();
break;
#if defined(OS_WIN) || defined(OS_LINUX)
case CBF_BITMAP:
if (!IsBitmapSafe(params))
return;
WriteBitmap(&(params[0].front()), &(params[1].front()));
break;
#endif // defined(OS_WIN) || defined(OS_LINUX)
default:
NOTREACHED();
}
}

Просмотреть файл

@ -0,0 +1,204 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CLIPBOARD_H_
#define BASE_CLIPBOARD_H_
#include <map>
#include <string>
#include <vector>
#include "base/file_path.h"
#include "base/process.h"
#include "base/string16.h"
#include "base/gfx/size.h"
class Clipboard {
public:
typedef std::string FormatType;
#if defined(OS_LINUX)
typedef struct _GtkClipboard GtkClipboard;
typedef std::map<FormatType, std::pair<char*, size_t> > TargetMap;
#endif
// ObjectType designates the type of data to be stored in the clipboard. This
// designation is shared across all OSes. The system-specific designation
// is defined by FormatType. A single ObjectType might be represented by
// several system-specific FormatTypes. For example, on Linux the CBF_TEXT
// ObjectType maps to "text/plain", "STRING", and several other formats. On
// windows it maps to CF_UNICODETEXT.
enum ObjectType {
CBF_TEXT,
CBF_HTML,
CBF_BOOKMARK,
CBF_LINK,
CBF_FILES,
CBF_WEBKIT,
CBF_BITMAP,
CBF_SMBITMAP // bitmap from shared memory
};
// ObjectMap is a map from ObjectType to associated data.
// The data is organized differently for each ObjectType. The following
// table summarizes what kind of data is stored for each key.
// * indicates an optional argument.
//
// Key Arguments Type
// -------------------------------------
// CBF_TEXT text char array
// CBF_HTML html char array
// url* char array
// CBF_BOOKMARK html char array
// url char array
// CBF_LINK html char array
// url char array
// CBF_FILES files char array representing multiple files.
// Filenames are separated by null characters and
// the final filename is double null terminated.
// The filenames are encoded in platform-specific
// encoding.
// CBF_WEBKIT none empty vector
// CBF_BITMAP pixels byte array
// size gfx::Size struct
// CBF_SMBITMAP shared_mem shared memory handle
// size gfx::Size struct
typedef std::vector<char> ObjectMapParam;
typedef std::vector<ObjectMapParam> ObjectMapParams;
typedef std::map<int /* ObjectType */, ObjectMapParams> ObjectMap;
Clipboard();
~Clipboard();
// Write a bunch of objects to the system clipboard. Copies are made of the
// contents of |objects|. On Windows they are copied to the system clipboard.
// On linux they are copied into a structure owned by the Clipboard object and
// kept until the system clipboard is set again.
void WriteObjects(const ObjectMap& objects);
// Behaves as above. If there is some shared memory handle passed as one of
// the objects, it came from the process designated by |process|. This will
// assist in turning it into a shared memory region that the current process
// can use.
void WriteObjects(const ObjectMap& objects, base::ProcessHandle process);
// Tests whether the clipboard contains a certain format
bool IsFormatAvailable(const FormatType& format) const;
// Reads UNICODE text from the clipboard, if available.
void ReadText(string16* result) const;
// Reads ASCII text from the clipboard, if available.
void ReadAsciiText(std::string* result) const;
// Reads HTML from the clipboard, if available.
void ReadHTML(string16* markup, std::string* src_url) const;
// Reads a bookmark from the clipboard, if available.
void ReadBookmark(string16* title, std::string* url) const;
// Reads a file or group of files from the clipboard, if available, into the
// out parameter.
void ReadFile(FilePath* file) const;
void ReadFiles(std::vector<FilePath>* files) const;
// Get format Identifiers for various types.
static FormatType GetUrlFormatType();
static FormatType GetUrlWFormatType();
static FormatType GetMozUrlFormatType();
static FormatType GetPlainTextFormatType();
static FormatType GetPlainTextWFormatType();
static FormatType GetFilenameFormatType();
static FormatType GetFilenameWFormatType();
static FormatType GetWebKitSmartPasteFormatType();
// Win: MS HTML Format, Other: Generic HTML format
static FormatType GetHtmlFormatType();
#if defined(OS_WIN)
static FormatType GetBitmapFormatType();
// Firefox text/html
static FormatType GetTextHtmlFormatType();
static FormatType GetCFHDropFormatType();
static FormatType GetFileDescriptorFormatType();
static FormatType GetFileContentFormatZeroType();
// Duplicates any remote shared memory handle embedded inside |objects| that
// was created by |process| so that it can be used by this process.
static void DuplicateRemoteHandles(base::ProcessHandle process,
ObjectMap* objects);
#endif
private:
void WriteText(const char* text_data, size_t text_len);
void WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len);
void WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len);
void WriteHyperlink(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len);
void WriteWebSmartPaste();
void WriteFiles(const char* file_data, size_t file_len);
void DispatchObject(ObjectType type, const ObjectMapParams& params);
void WriteBitmap(const char* pixel_data, const char* size_data);
#if defined(OS_WIN)
void WriteBitmapFromSharedMemory(const char* bitmap_data,
const char* size_data,
base::ProcessHandle handle);
void WriteBitmapFromHandle(HBITMAP source_hbitmap,
const gfx::Size& size);
// Safely write to system clipboard. Free |handle| on failure.
void WriteToClipboard(unsigned int format, HANDLE handle);
static void ParseBookmarkClipboardFormat(const string16& bookmark,
string16* title,
std::string* url);
// Free a handle depending on its type (as intuited from format)
static void FreeData(unsigned int format, HANDLE data);
// Return the window that should be the clipboard owner, creating it
// if neccessary. Marked const for lazily initialization by const methods.
HWND GetClipboardWindow() const;
// Mark this as mutable so const methods can still do lazy initialization.
mutable HWND clipboard_owner_;
// True if we can create a window.
bool create_window_;
#elif defined(OS_LINUX)
// Data is stored in the |clipboard_data_| map until it is saved to the system
// clipboard. The Store* functions save data to the |clipboard_data_| map. The
// SetGtkClipboard function replaces whatever is on the system clipboard with
// the contents of |clipboard_data_|.
// The Write* functions make a deep copy of the data passed to them an store
// it in |clipboard_data_|.
// Write changes to gtk clipboard.
void SetGtkClipboard();
// Free pointers in clipboard_data_ and clear() the map.
void FreeTargetMap();
// Insert a mapping into clipboard_data_.
void InsertMapping(const char* key, char* data, size_t data_len);
TargetMap* clipboard_data_;
GtkClipboard* clipboard_;
#endif
DISALLOW_EVIL_CONSTRUCTORS(Clipboard);
};
#endif // BASE_CLIPBOARD_H_

Просмотреть файл

@ -0,0 +1,328 @@
// Copyright (c) 2006-2008 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/clipboard.h"
#include <gtk/gtk.h>
#include <map>
#include <set>
#include <string>
#include <utility>
#include "base/scoped_ptr.h"
#include "base/linux_util.h"
#include "base/string_util.h"
namespace {
const char kMimeBmp[] = "image/bmp";
const char kMimeHtml[] = "text/html";
const char kMimeText[] = "text/plain";
const char kMimeWebkitSmartPaste[] = "chromium-internal/webkit-paste";
std::string GdkAtomToString(const GdkAtom& atom) {
gchar* name = gdk_atom_name(atom);
std::string rv(name);
g_free(name);
return rv;
}
GdkAtom StringToGdkAtom(const std::string& str) {
return gdk_atom_intern(str.c_str(), false);
}
// GtkClipboardGetFunc callback.
// GTK will call this when an application wants data we copied to the clipboard.
void GetData(GtkClipboard* clipboard,
GtkSelectionData* selection_data,
guint info,
gpointer user_data) {
Clipboard::TargetMap* data_map =
reinterpret_cast<Clipboard::TargetMap*>(user_data);
std::string target_string = GdkAtomToString(selection_data->target);
Clipboard::TargetMap::iterator iter = data_map->find(target_string);
if (iter == data_map->end())
return;
if (target_string == kMimeBmp) {
gtk_selection_data_set_pixbuf(selection_data,
reinterpret_cast<GdkPixbuf*>(iter->second.first));
} else {
gtk_selection_data_set(selection_data, selection_data->target, 8,
reinterpret_cast<guchar*>(iter->second.first),
iter->second.second);
}
}
// GtkClipboardClearFunc callback.
// We are guaranteed this will be called exactly once for each call to
// gtk_clipboard_set_with_data
void ClearData(GtkClipboard* clipboard,
gpointer user_data) {
Clipboard::TargetMap* map =
reinterpret_cast<Clipboard::TargetMap*>(user_data);
std::set<char*> ptrs;
for (Clipboard::TargetMap::iterator iter = map->begin();
iter != map->end(); ++iter) {
if (iter->first == kMimeBmp)
g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
else
ptrs.insert(iter->second.first);
}
for (std::set<char*>::iterator iter = ptrs.begin();
iter != ptrs.end(); ++iter)
delete[] *iter;
delete map;
}
// Frees the pointers in the given map and clears the map.
// Does not double-free any pointers.
void FreeTargetMap(Clipboard::TargetMap map) {
std::set<char*> ptrs;
for (Clipboard::TargetMap::iterator iter = map.begin();
iter != map.end(); ++iter)
ptrs.insert(iter->second.first);
for (std::set<char*>::iterator iter = ptrs.begin();
iter != ptrs.end(); ++iter)
delete[] *iter;
map.clear();
}
// Called on GdkPixbuf destruction; see WriteBitmap().
void GdkPixbufFree(guchar* pixels, gpointer data) {
free(pixels);
}
} // namespace
Clipboard::Clipboard() {
clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
}
Clipboard::~Clipboard() {
// TODO(estade): do we want to save clipboard data after we exit?
// gtk_clipboard_set_can_store and gtk_clipboard_store work
// but have strangely awful performance.
}
void Clipboard::WriteObjects(const ObjectMap& objects) {
clipboard_data_ = new TargetMap();
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
SetGtkClipboard();
}
// Take ownership of the GTK clipboard and inform it of the targets we support.
void Clipboard::SetGtkClipboard() {
scoped_array<GtkTargetEntry> targets(
new GtkTargetEntry[clipboard_data_->size()]);
int i = 0;
for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin();
iter != clipboard_data_->end(); ++iter, ++i) {
char* target_string = new char[iter->first.size() + 1];
strcpy(target_string, iter->first.c_str());
targets[i].target = target_string;
targets[i].flags = 0;
targets[i].info = i;
}
gtk_clipboard_set_with_data(clipboard_, targets.get(),
clipboard_data_->size(),
GetData, ClearData,
clipboard_data_);
for (size_t i = 0; i < clipboard_data_->size(); i++)
delete[] targets[i].target;
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
char* data = new char[text_len];
memcpy(data, text_data, text_len);
InsertMapping(kMimeText, data, text_len);
InsertMapping("TEXT", data, text_len);
InsertMapping("STRING", data, text_len);
InsertMapping("UTF8_STRING", data, text_len);
InsertMapping("COMPOUND_TEXT", data, text_len);
}
void Clipboard::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
// TODO(estade): might not want to ignore |url_data|
char* data = new char[markup_len];
memcpy(data, markup_data, markup_len);
InsertMapping(kMimeHtml, data, markup_len);
}
// Write an extra flavor that signifies WebKit was the last to modify the
// pasteboard. This flavor has no data.
void Clipboard::WriteWebSmartPaste() {
InsertMapping(kMimeWebkitSmartPaste, NULL, 0);
}
void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
guchar* data = base::BGRAToRGBA(reinterpret_cast<const uint8_t*>(pixel_data),
size->width(), size->height(), 0);
GdkPixbuf* pixbuf =
gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE,
8, size->width(), size->height(),
size->width() * 4, GdkPixbufFree, NULL);
// We store the GdkPixbuf*, and the size_t half of the pair is meaningless.
// Note that this contrasts with the vast majority of entries in our target
// map, which directly store the data and its length.
InsertMapping(kMimeBmp, reinterpret_cast<char*>(pixbuf), 0);
}
void Clipboard::WriteBookmark(const char* title_data, size_t title_len,
const char* url_data, size_t url_len) {
// TODO(estade): implement this, but for now fail silently so we do not
// write error output during layout tests.
// NOTIMPLEMENTED();
}
void Clipboard::WriteHyperlink(const char* title_data, size_t title_len,
const char* url_data, size_t url_len) {
NOTIMPLEMENTED();
}
void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
NOTIMPLEMENTED();
}
// We do not use gtk_clipboard_wait_is_target_available because of
// a bug with the gtk clipboard. It caches the available targets
// and does not always refresh the cache when it is appropriate.
// TODO(estade): When gnome bug 557315 is resolved, change this function
// to use gtk_clipboard_wait_is_target_available. Also, catch requests
// for plain text and change them to gtk_clipboard_wait_is_text_available
// (which checks for several standard text targets).
bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const {
bool retval = false;
GdkAtom* targets = NULL;
GtkSelectionData* data =
gtk_clipboard_wait_for_contents(clipboard_,
gdk_atom_intern("TARGETS", false));
if (!data)
return false;
int num = 0;
gtk_selection_data_get_targets(data, &targets, &num);
GdkAtom format_atom = StringToGdkAtom(format);
for (int i = 0; i < num; i++) {
if (targets[i] == format_atom) {
retval = true;
break;
}
}
gtk_selection_data_free(data);
g_free(targets);
return retval;
}
void Clipboard::ReadText(string16* result) const {
result->clear();
gchar* text = gtk_clipboard_wait_for_text(clipboard_);
if (text == NULL)
return;
// TODO(estade): do we want to handle the possible error here?
UTF8ToUTF16(text, strlen(text), result);
g_free(text);
}
void Clipboard::ReadAsciiText(std::string* result) const {
result->clear();
gchar* text = gtk_clipboard_wait_for_text(clipboard_);
if (text == NULL)
return;
result->assign(text);
g_free(text);
}
void Clipboard::ReadFile(FilePath* file) const {
*file = FilePath();
}
// TODO(estade): handle different charsets.
void Clipboard::ReadHTML(string16* markup, std::string* src_url) const {
markup->clear();
GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard_,
StringToGdkAtom(GetHtmlFormatType()));
if (!data)
return;
UTF8ToUTF16(reinterpret_cast<char*>(data->data),
strlen(reinterpret_cast<char*>(data->data)),
markup);
gtk_selection_data_free(data);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
return GdkAtomToString(GDK_TARGET_STRING);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
return GetPlainTextFormatType();
}
// static
Clipboard::FormatType Clipboard::GetHtmlFormatType() {
return std::string(kMimeHtml);
}
// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
return std::string(kMimeWebkitSmartPaste);
}
// Insert the key/value pair in the clipboard_data structure. If
// the mapping already exists, it frees the associated data. Don't worry
// about double freeing because if the same key is inserted into the
// map twice, it must have come from different Write* functions and the
// data pointer cannot be the same.
void Clipboard::InsertMapping(const char* key,
char* data,
size_t data_len) {
TargetMap::iterator iter = clipboard_data_->find(key);
if (iter != clipboard_data_->end()) {
if (strcmp(kMimeBmp, key) == 0)
g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
else
delete[] iter->second.first;
}
(*clipboard_data_)[key] = std::make_pair(data, data_len);
}

Просмотреть файл

@ -0,0 +1,283 @@
// Copyright (c) 2006-2008 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/clipboard.h"
#import <Cocoa/Cocoa.h>
#include "base/logging.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
namespace {
// Would be nice if this were in UTCoreTypes.h, but it isn't
const NSString* kUTTypeURLName = @"public.url-name";
// Tells us if WebKit was the last to write to the pasteboard. There's no
// actual data associated with this type.
const NSString *kWebSmartPastePboardType = @"NeXT smart paste pasteboard type";
NSPasteboard* GetPasteboard() {
// The pasteboard should not be nil in a UI session, but this handy DCHECK
// can help track down problems if someone tries using clipboard code outside
// of a UI session.
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
DCHECK(pasteboard);
return pasteboard;
}
} // namespace
Clipboard::Clipboard() {
}
Clipboard::~Clipboard() {
}
void Clipboard::WriteObjects(const ObjectMap& objects) {
NSPasteboard* pb = GetPasteboard();
[pb declareTypes:[NSArray array] owner:nil];
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
std::string text_str(text_data, text_len);
NSString *text = base::SysUTF8ToNSString(text_str);
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
[pb setString:text forType:NSStringPboardType];
}
void Clipboard::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
std::string html_fragment_str(markup_data, markup_len);
NSString *html_fragment = base::SysUTF8ToNSString(html_fragment_str);
// TODO(avi): url_data?
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSHTMLPboardType] owner:nil];
[pb setString:html_fragment forType:NSHTMLPboardType];
}
void Clipboard::WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
WriteHyperlink(title_data, title_len, url_data, url_len);
}
void Clipboard::WriteHyperlink(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
std::string title_str(title_data, title_len);
NSString *title = base::SysUTF8ToNSString(title_str);
std::string url_str(url_data, url_len);
NSString *url = base::SysUTF8ToNSString(url_str);
// TODO(playmobil): In the Windows version of this function, an HTML
// representation of the bookmark is also added to the clipboard, to support
// drag and drop of web shortcuts. I don't think we need to do this on the
// Mac, but we should double check later on.
NSURL* nsurl = [NSURL URLWithString:url];
NSPasteboard* pb = GetPasteboard();
// passing UTIs into the pasteboard methods is valid >= 10.5
[pb addTypes:[NSArray arrayWithObjects:NSURLPboardType,
kUTTypeURLName,
nil]
owner:nil];
[nsurl writeToPasteboard:pb];
[pb setString:title forType:kUTTypeURLName];
}
void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:1];
// Offset of current filename from start of file_data array.
size_t current_filename_offset = 0;
// file_data is double null terminated (see table at top of clipboard.h).
// So this loop can ignore the second null terminator, thus file_len - 1.
// TODO(playmobil): If we need a loop like this on other platforms then split
// this out into a common function that outputs a std::vector<const char*>.
for (size_t i = 0; i < file_len - 1; ++i) {
if (file_data[i] == '\0') {
const char* filename = &file_data[current_filename_offset];
[fileList addObject:[NSString stringWithUTF8String:filename]];
current_filename_offset = i + 1;
continue;
}
}
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
[pb setPropertyList:fileList forType:NSFilenamesPboardType];
}
// Write an extra flavor that signifies WebKit was the last to modify the
// pasteboard. This flavor has no data.
void Clipboard::WriteWebSmartPaste() {
NSPasteboard* pb = GetPasteboard();
NSString* format = base::SysUTF8ToNSString(GetWebKitSmartPasteFormatType());
[pb addTypes:[NSArray arrayWithObject:format] owner:nil];
[pb setData:nil forType:format];
}
bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const {
NSString* format_ns = base::SysUTF8ToNSString(format);
NSPasteboard* pb = GetPasteboard();
NSArray* types = [pb types];
return [types containsObject:format_ns];
}
void Clipboard::ReadText(string16* result) const {
NSPasteboard* pb = GetPasteboard();
NSString* contents = [pb stringForType:NSStringPboardType];
UTF8ToUTF16([contents UTF8String],
[contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
result);
}
void Clipboard::ReadAsciiText(std::string* result) const {
NSPasteboard* pb = GetPasteboard();
NSString* contents = [pb stringForType:NSStringPboardType];
if (!contents)
result->clear();
else
result->assign([contents UTF8String]);
}
void Clipboard::ReadHTML(string16* markup, std::string* src_url) const {
if (markup) {
NSPasteboard* pb = GetPasteboard();
NSArray *supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType,
NSStringPboardType,
nil];
NSString *bestType = [pb availableTypeFromArray:supportedTypes];
NSString* contents = [pb stringForType:bestType];
UTF8ToUTF16([contents UTF8String],
[contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
markup);
}
// TODO(avi): src_url?
if (src_url)
src_url->clear();
}
void Clipboard::ReadBookmark(string16* title, std::string* url) const {
NSPasteboard* pb = GetPasteboard();
if (title) {
NSString* contents = [pb stringForType:kUTTypeURLName];
UTF8ToUTF16([contents UTF8String],
[contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
title);
}
if (url) {
NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString];
if (!url_string)
url->clear();
else
url->assign([url_string UTF8String]);
}
}
void Clipboard::ReadFile(FilePath* file) const {
if (!file) {
NOTREACHED();
return;
}
*file = FilePath();
std::vector<FilePath> files;
ReadFiles(&files);
// Take the first file, if available.
if (!files.empty())
*file = files[0];
}
void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
if (!files) {
NOTREACHED();
return;
}
files->clear();
NSPasteboard* pb = GetPasteboard();
NSArray* fileList = [pb propertyListForType:NSFilenamesPboardType];
for (unsigned int i = 0; i < [fileList count]; ++i) {
std::string file = [[fileList objectAtIndex:i] UTF8String];
files->push_back(FilePath(file));
}
}
// static
Clipboard::FormatType Clipboard::GetUrlFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSURLPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetUrlWFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSURLPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSStringPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSStringPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetFilenameFormatType() {
static const std::string type =
base::SysNSStringToUTF8(NSFilenamesPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
static const std::string type =
base::SysNSStringToUTF8(NSFilenamesPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetHtmlFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSHTMLPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
static const std::string type =
base::SysNSStringToUTF8(kWebSmartPastePboardType);
return type;
}

Просмотреть файл

@ -0,0 +1,282 @@
// Copyright (c) 2006-2008 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 <string>
#include "base/basictypes.h"
#include "base/clipboard.h"
#include "base/message_loop.h"
#include "base/scoped_clipboard_writer.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#if defined(OS_WIN)
class ClipboardTest : public PlatformTest {
protected:
virtual void SetUp() {
message_loop_.reset(new MessageLoopForUI());
}
virtual void TearDown() {
}
private:
scoped_ptr<MessageLoop> message_loop_;
};
#elif defined(OS_POSIX)
typedef PlatformTest ClipboardTest;
#endif // defined(OS_WIN)
TEST_F(ClipboardTest, ClearTest) {
Clipboard clipboard;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteText(ASCIIToUTF16("clear me"));
}
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(ASCIIToUTF16("<b>broom</b>"), "");
}
EXPECT_FALSE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextWFormatType()));
EXPECT_FALSE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextFormatType()));
}
TEST_F(ClipboardTest, TextTest) {
Clipboard clipboard;
string16 text(ASCIIToUTF16("This is a string16!#$")), text_result;
std::string ascii_text;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteText(text);
}
EXPECT_TRUE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextWFormatType()));
EXPECT_TRUE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextFormatType()));
clipboard.ReadText(&text_result);
EXPECT_EQ(text, text_result);
clipboard.ReadAsciiText(&ascii_text);
EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}
TEST_F(ClipboardTest, HTMLTest) {
Clipboard clipboard;
string16 markup(ASCIIToUTF16("<string>Hi!</string>")), markup_result;
std::string url("http://www.example.com/"), url_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(markup, url);
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetHtmlFormatType()));
clipboard.ReadHTML(&markup_result, &url_result);
EXPECT_EQ(markup, markup_result);
#if defined(OS_WIN)
// TODO(playmobil): It's not clear that non windows clipboards need to support
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
}
TEST_F(ClipboardTest, TrickyHTMLTest) {
Clipboard clipboard;
string16 markup(ASCIIToUTF16("<em>Bye!<!--EndFragment --></em>")),
markup_result;
std::string url, url_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(markup, url);
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetHtmlFormatType()));
clipboard.ReadHTML(&markup_result, &url_result);
EXPECT_EQ(markup, markup_result);
#if defined(OS_WIN)
// TODO(playmobil): It's not clear that non windows clipboards need to support
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
}
// TODO(estade): Port the following test (decide what target we use for urls)
#if !defined(OS_LINUX)
TEST_F(ClipboardTest, BookmarkTest) {
Clipboard clipboard;
string16 title(ASCIIToUTF16("The Example Company")), title_result;
std::string url("http://www.example.com/"), url_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteBookmark(title, url);
}
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType()));
clipboard.ReadBookmark(&title_result, &url_result);
EXPECT_EQ(title, title_result);
EXPECT_EQ(url, url_result);
}
#endif // defined(OS_WIN)
TEST_F(ClipboardTest, MultiFormatTest) {
Clipboard clipboard;
string16 text(ASCIIToUTF16("Hi!")), text_result;
string16 markup(ASCIIToUTF16("<strong>Hi!</string>")), markup_result;
std::string url("http://www.example.com/"), url_result;
std::string ascii_text;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(markup, url);
clipboard_writer.WriteText(text);
}
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType()));
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetPlainTextWFormatType()));
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetPlainTextFormatType()));
clipboard.ReadHTML(&markup_result, &url_result);
EXPECT_EQ(markup, markup_result);
#if defined(OS_WIN)
// TODO(playmobil): It's not clear that non windows clipboards need to support
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
clipboard.ReadText(&text_result);
EXPECT_EQ(text, text_result);
clipboard.ReadAsciiText(&ascii_text);
EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}
// TODO(estade): Port the following tests (decide what targets we use for files)
#if !defined(OS_LINUX)
// Files for this test don't actually need to exist on the file system, just
// don't try to use a non-existent file you've retrieved from the clipboard.
TEST_F(ClipboardTest, FileTest) {
Clipboard clipboard;
#if defined(OS_WIN)
FilePath file(L"C:\\Downloads\\My Downloads\\A Special File.txt");
#elif defined(OS_MACOSX)
// OS X will print a warning message if we stick a non-existant file on the
// clipboard.
FilePath file("/usr/bin/make");
#endif // defined(OS_MACOSX)
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteFile(file);
}
FilePath out_file;
clipboard.ReadFile(&out_file);
EXPECT_EQ(file.value(), out_file.value());
}
TEST_F(ClipboardTest, MultipleFilesTest) {
Clipboard clipboard;
#if defined(OS_WIN)
FilePath file1(L"C:\\Downloads\\My Downloads\\File 1.exe");
FilePath file2(L"C:\\Downloads\\My Downloads\\File 2.pdf");
FilePath file3(L"C:\\Downloads\\My Downloads\\File 3.doc");
#elif defined(OS_MACOSX)
// OS X will print a warning message if we stick a non-existant file on the
// clipboard.
FilePath file1("/usr/bin/make");
FilePath file2("/usr/bin/man");
FilePath file3("/usr/bin/perl");
#endif // defined(OS_MACOSX)
std::vector<FilePath> files;
files.push_back(file1);
files.push_back(file2);
files.push_back(file3);
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteFiles(files);
}
std::vector<FilePath> out_files;
clipboard.ReadFiles(&out_files);
EXPECT_EQ(files.size(), out_files.size());
for (size_t i = 0; i < out_files.size(); ++i)
EXPECT_EQ(files[i].value(), out_files[i].value());
}
#endif // !defined(OS_LINUX)
#if defined(OS_WIN) // Windows only tests.
TEST_F(ClipboardTest, HyperlinkTest) {
Clipboard clipboard;
string16 title(ASCIIToUTF16("The Example Company")), title_result;
std::string url("http://www.example.com/"), url_result;
string16 html(ASCIIToUTF16("<a href=\"http://www.example.com/\">"
"The Example Company</a>")), html_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHyperlink(title, url);
}
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType()));
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType()));
clipboard.ReadBookmark(&title_result, &url_result);
EXPECT_EQ(title, title_result);
EXPECT_EQ(url, url_result);
clipboard.ReadHTML(&html_result, &url_result);
EXPECT_EQ(html, html_result);
}
TEST_F(ClipboardTest, WebSmartPasteTest) {
Clipboard clipboard;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteWebSmartPaste();
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetWebKitSmartPasteFormatType()));
}
TEST_F(ClipboardTest, BitmapTest) {
unsigned int fake_bitmap[] = {
0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
};
Clipboard clipboard;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteBitmapFromPixels(fake_bitmap, gfx::Size(3, 4));
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetBitmapFormatType()));
}
#endif // defined(OS_WIN)

Просмотреть файл

@ -0,0 +1,488 @@
// Copyright (c) 2006-2008 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/clipboard_util.h"
#include <shellapi.h>
#include <shlwapi.h>
#include <wininet.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/scoped_handle.h"
#include "base/string_util.h"
namespace {
bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url,
std::wstring* title) {
DCHECK(data_object && url && title);
STGMEDIUM medium;
if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium)))
return false;
HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
if (!hdrop)
return false;
bool success = false;
wchar_t filename[MAX_PATH];
if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
arraysize(url_buffer), filename)) {
*url = url_buffer;
PathRemoveExtension(filename);
title->assign(PathFindFileName(filename));
success = true;
}
}
DragFinish(hdrop);
GlobalUnlock(medium.hGlobal);
// We don't need to call ReleaseStgMedium here because as far as I can tell,
// DragFinish frees the hGlobal for us.
return success;
}
bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url,
std::wstring* title) {
DCHECK(url && title);
size_t newline_pos = str.find('\n');
bool success = false;
if (newline_pos != std::string::npos) {
*url = str.substr(0, newline_pos);
title->assign(str.substr(newline_pos + 1));
success = true;
} else {
*url = str;
title->assign(str);
success = true;
}
return success;
}
} // namespace
FORMATETC* ClipboardUtil::GetUrlFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetUrlWFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetMozUrlFormat() {
// The format is "URL\nTitle"
static UINT cf = RegisterClipboardFormat(L"text/x-moz-url");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetPlainTextFormat() {
// We don't need to register this format since it's a built in format.
static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetPlainTextWFormat() {
// We don't need to register this format since it's a built in format.
static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1,
TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFilenameWFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFilenameFormat()
{
static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetHtmlFormat() {
static UINT cf = RegisterClipboardFormat(L"HTML Format");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
static UINT cf = RegisterClipboardFormat(L"text/html");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetCFHDropFormat() {
// We don't need to register this format since it's a built in format.
static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
return &format;
}
bool ClipboardUtil::HasUrl(IDataObject* data_object) {
DCHECK(data_object);
return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
}
bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
DCHECK(data_object);
return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
}
bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
DCHECK(data_object);
return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
}
bool ClipboardUtil::GetUrl(IDataObject* data_object,
std::wstring* url, std::wstring* title) {
DCHECK(data_object && url && title);
if (!HasUrl(data_object))
return false;
// Try to extract a URL from |data_object| in a variety of formats.
STGMEDIUM store;
if (GetUrlFromHDrop(data_object, url, title)) {
return true;
}
if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
// Mozilla URL format or unicode URL
ScopedHGlobal<wchar_t> data(store.hGlobal);
bool success = SplitUrlAndTitle(data.get(), url, title);
ReleaseStgMedium(&store);
if (success)
return true;
}
if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
// URL using ascii
ScopedHGlobal<char> data(store.hGlobal);
bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
ReleaseStgMedium(&store);
if (success)
return true;
}
if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
// filename using unicode
ScopedHGlobal<wchar_t> data(store.hGlobal);
bool success = false;
if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
PathIsUNC(data.get()))) {
wchar_t file_url[INTERNET_MAX_URL_LENGTH];
DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
0))) {
*url = file_url;
title->assign(file_url);
success = true;
}
}
ReleaseStgMedium(&store);
if (success)
return true;
}
if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
// filename using ascii
ScopedHGlobal<char> data(store.hGlobal);
bool success = false;
if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
PathIsUNCA(data.get()))) {
char file_url[INTERNET_MAX_URL_LENGTH];
DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
if (SUCCEEDED(::UrlCreateFromPathA(data.get(),
file_url,
&file_url_len,
0))) {
*url = UTF8ToWide(file_url);
title->assign(*url);
success = true;
}
}
ReleaseStgMedium(&store);
if (success)
return true;
}
return false;
}
bool ClipboardUtil::GetFilenames(IDataObject* data_object,
std::vector<std::wstring>* filenames) {
DCHECK(data_object && filenames);
if (!HasFilenames(data_object))
return false;
STGMEDIUM medium;
if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
return false;
HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
if (!hdrop)
return false;
const int kMaxFilenameLen = 4096;
const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
for (unsigned int i = 0; i < num_files; ++i) {
wchar_t filename[kMaxFilenameLen];
if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
continue;
filenames->push_back(filename);
}
DragFinish(hdrop);
GlobalUnlock(medium.hGlobal);
// We don't need to call ReleaseStgMedium here because as far as I can tell,
// DragFinish frees the hGlobal for us.
return true;
}
bool ClipboardUtil::GetPlainText(IDataObject* data_object,
std::wstring* plain_text) {
DCHECK(data_object && plain_text);
if (!HasPlainText(data_object))
return false;
STGMEDIUM store;
bool success = false;
if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
// Unicode text
ScopedHGlobal<wchar_t> data(store.hGlobal);
plain_text->assign(data.get());
ReleaseStgMedium(&store);
success = true;
} else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
// ascii text
ScopedHGlobal<char> data(store.hGlobal);
plain_text->assign(UTF8ToWide(data.get()));
ReleaseStgMedium(&store);
success = true;
} else {
//If a file is dropped on the window, it does not provide either of the
//plain text formats, so here we try to forcibly get a url.
std::wstring title;
success = GetUrl(data_object, plain_text, &title);
}
return success;
}
bool ClipboardUtil::GetHtml(IDataObject* data_object,
std::wstring* html, std::string* base_url) {
DCHECK(data_object && html && base_url);
if (SUCCEEDED(data_object->QueryGetData(GetHtmlFormat()))) {
STGMEDIUM store;
if (SUCCEEDED(data_object->GetData(GetHtmlFormat(), &store))) {
// MS CF html
ScopedHGlobal<char> data(store.hGlobal);
std::string html_utf8;
CFHtmlToHtml(std::string(data.get(), data.Size()), &html_utf8, base_url);
html->assign(UTF8ToWide(html_utf8));
ReleaseStgMedium(&store);
return true;
}
}
if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
return false;
STGMEDIUM store;
if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
return false;
// text/html
ScopedHGlobal<wchar_t> data(store.hGlobal);
html->assign(data.get());
ReleaseStgMedium(&store);
return true;
}
bool ClipboardUtil::GetFileContents(IDataObject* data_object,
std::wstring* filename, std::string* file_contents) {
DCHECK(data_object && filename && file_contents);
bool has_data =
SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
if (!has_data)
return false;
STGMEDIUM content;
// The call to GetData can be very slow depending on what is in
// |data_object|.
if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
if (TYMED_HGLOBAL == content.tymed) {
ScopedHGlobal<char> data(content.hGlobal);
file_contents->assign(data.get(), data.Size());
}
ReleaseStgMedium(&content);
}
STGMEDIUM description;
if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
&description))) {
ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
// We expect there to be at least one file in here.
DCHECK(fgd->cItems >= 1);
filename->assign(fgd->fgd[0].cFileName);
ReleaseStgMedium(&description);
}
return true;
}
// HtmlToCFHtml and CFHtmlToHtml are based on similar methods in
// WebCore/platform/win/ClipboardUtilitiesWin.cpp.
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Helper method for converting from text/html to MS CF_HTML.
// Documentation for the CF_HTML format is available at
// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
std::string ClipboardUtil::HtmlToCFHtml(const std::string& html,
const std::string& base_url) {
if (html.empty())
return std::string();
#define MAX_DIGITS 10
#define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
#define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
#define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
static const char* header = "Version:0.9\r\n"
"StartHTML:" NUMBER_FORMAT "\r\n"
"EndHTML:" NUMBER_FORMAT "\r\n"
"StartFragment:" NUMBER_FORMAT "\r\n"
"EndFragment:" NUMBER_FORMAT "\r\n";
static const char* source_url_prefix = "SourceURL:";
static const char* start_markup =
"<html>\r\n<body>\r\n<!--StartFragment-->\r\n";
static const char* end_markup =
"\r\n<!--EndFragment-->\r\n</body>\r\n</html>";
// Calculate offsets
size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 +
MAX_DIGITS * 4;
if (!base_url.empty()) {
start_html_offset += strlen(source_url_prefix) +
base_url.length() + 2; // Add 2 for \r\n.
}
size_t start_fragment_offset = start_html_offset + strlen(start_markup);
size_t end_fragment_offset = start_fragment_offset + html.length();
size_t end_html_offset = end_fragment_offset + strlen(end_markup);
std::string result = StringPrintf(header, start_html_offset,
end_html_offset, start_fragment_offset, end_fragment_offset);
if (!base_url.empty()) {
result.append(source_url_prefix);
result.append(base_url);
result.append("\r\n");
}
result.append(start_markup);
result.append(html);
result.append(end_markup);
#undef MAX_DIGITS
#undef MAKE_NUMBER_FORMAT_1
#undef MAKE_NUMBER_FORMAT_2
#undef NUMBER_FORMAT
return result;
}
// Helper method for converting from MS CF_HTML to text/html.
void ClipboardUtil::CFHtmlToHtml(const std::string& cf_html,
std::string* html,
std::string* base_url) {
// Obtain base_url if present.
static std::string src_url_str("SourceURL:");
size_t line_start = cf_html.find(src_url_str);
if (line_start != std::string::npos) {
size_t src_end = cf_html.find("\n", line_start);
size_t src_start = line_start + src_url_str.length();
if (src_end != std::string::npos && src_start != std::string::npos) {
*base_url = cf_html.substr(src_start, src_end - src_start);
TrimWhitespace(*base_url, TRIM_ALL, base_url);
}
}
// Find the markup between "<!--StartFragment -->" and "<!--EndFragment-->".
std::string cf_html_lower = StringToLowerASCII(cf_html);
size_t markup_start = cf_html_lower.find("<html", 0);
size_t tag_start = cf_html.find("StartFragment", markup_start);
size_t fragment_start = cf_html.find('>', tag_start) + 1;
size_t tag_end = cf_html.rfind("EndFragment", std::string::npos);
size_t fragment_end = cf_html.rfind('<', tag_end);
if (fragment_start != std::string::npos &&
fragment_end != std::string::npos) {
*html = cf_html.substr(fragment_start, fragment_end - fragment_start);
TrimWhitespace(*html, TRIM_ALL, html);
}
}

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) 2006-2008 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.
//
// Some helper functions for working with the clipboard and IDataObjects.
#ifndef BASE_CLIPBOARD_UTIL_H_
#define BASE_CLIPBOARD_UTIL_H_
#include <shlobj.h>
#include <string>
#include <vector>
class ClipboardUtil {
public:
/////////////////////////////////////////////////////////////////////////////
// Clipboard formats.
static FORMATETC* GetUrlFormat();
static FORMATETC* GetUrlWFormat();
static FORMATETC* GetMozUrlFormat();
static FORMATETC* GetPlainTextFormat();
static FORMATETC* GetPlainTextWFormat();
static FORMATETC* GetFilenameFormat();
static FORMATETC* GetFilenameWFormat();
// MS HTML Format
static FORMATETC* GetHtmlFormat();
// Firefox text/html
static FORMATETC* GetTextHtmlFormat();
static FORMATETC* GetCFHDropFormat();
static FORMATETC* GetFileDescriptorFormat();
static FORMATETC* GetFileContentFormatZero();
static FORMATETC* GetWebKitSmartPasteFormat();
/////////////////////////////////////////////////////////////////////////////
// These methods check to see if |data_object| has the requested type.
// Returns true if it does.
static bool HasUrl(IDataObject* data_object);
static bool HasFilenames(IDataObject* data_object);
static bool HasPlainText(IDataObject* data_object);
/////////////////////////////////////////////////////////////////////////////
// Helper methods to extract information from an IDataObject. These methods
// return true if the requested data type is found in |data_object|.
static bool GetUrl(IDataObject* data_object,
std::wstring* url, std::wstring* title);
static bool GetFilenames(IDataObject* data_object,
std::vector<std::wstring>* filenames);
static bool GetPlainText(IDataObject* data_object, std::wstring* plain_text);
static bool GetHtml(IDataObject* data_object, std::wstring* text_html,
std::string* base_url);
static bool GetFileContents(IDataObject* data_object,
std::wstring* filename,
std::string* file_contents);
// A helper method for converting between MS CF_HTML format and plain
// text/html.
static std::string HtmlToCFHtml(const std::string& html,
const std::string& base_url);
static void CFHtmlToHtml(const std::string& cf_html, std::string* html,
std::string* base_url);
};
#endif // BASE_CLIPBOARD_UTIL_H_

Просмотреть файл

@ -0,0 +1,650 @@
// Copyright (c) 2006-2008 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.
// Many of these functions are based on those found in
// webkit/port/platform/PasteboardWin.cpp
#include "base/clipboard.h"
#include <shlobj.h>
#include <shellapi.h>
#include "base/clipboard_util.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/shared_memory.h"
#include "base/string_util.h"
namespace {
// A scoper to manage acquiring and automatically releasing the clipboard.
class ScopedClipboard {
public:
ScopedClipboard() : opened_(false) { }
~ScopedClipboard() {
if (opened_)
Release();
}
bool Acquire(HWND owner) {
const int kMaxAttemptsToOpenClipboard = 5;
if (opened_) {
NOTREACHED();
return false;
}
// Attempt to open the clipboard, which will acquire the Windows clipboard
// lock. This may fail if another process currently holds this lock.
// We're willing to try a few times in the hopes of acquiring it.
//
// This turns out to be an issue when using remote desktop because the
// rdpclip.exe process likes to read what we've written to the clipboard and
// send it to the RDP client. If we open and close the clipboard in quick
// succession, we might be trying to open it while rdpclip.exe has it open,
// See Bug 815425.
//
// In fact, we believe we'll only spin this loop over remote desktop. In
// normal situations, the user is initiating clipboard operations and there
// shouldn't be contention.
for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
// If we didn't manage to open the clipboard, sleep a bit and be hopeful.
if (attempts != 0)
::Sleep(5);
if (::OpenClipboard(owner)) {
opened_ = true;
return true;
}
}
// We failed to acquire the clipboard.
return false;
}
void Release() {
if (opened_) {
::CloseClipboard();
opened_ = false;
} else {
NOTREACHED();
}
}
private:
bool opened_;
};
LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
LRESULT lresult = 0;
switch (message) {
case WM_RENDERFORMAT:
// This message comes when SetClipboardData was sent a null data handle
// and now it's come time to put the data on the clipboard.
// We always set data, so there isn't a need to actually do anything here.
break;
case WM_RENDERALLFORMATS:
// This message comes when SetClipboardData was sent a null data handle
// and now this application is about to quit, so it must put data on
// the clipboard before it exits.
// We always set data, so there isn't a need to actually do anything here.
break;
case WM_DRAWCLIPBOARD:
break;
case WM_DESTROY:
break;
case WM_CHANGECBCHAIN:
break;
default:
lresult = DefWindowProc(hwnd, message, wparam, lparam);
break;
}
return lresult;
}
template <typename charT>
HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
HGLOBAL data =
::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
if (data) {
charT* raw_data = static_cast<charT*>(::GlobalLock(data));
memcpy(raw_data, str.data(), str.size() * sizeof(charT));
raw_data[str.size()] = '\0';
::GlobalUnlock(data);
}
return data;
};
} // namespace
Clipboard::Clipboard() : create_window_(false) {
if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) {
// Make a dummy HWND to be the clipboard's owner.
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = ClipboardOwnerWndProc;
wcex.hInstance = GetModuleHandle(NULL);
wcex.lpszClassName = L"ClipboardOwnerWindowClass";
::RegisterClassEx(&wcex);
create_window_ = true;
}
clipboard_owner_ = NULL;
}
Clipboard::~Clipboard() {
if (clipboard_owner_)
::DestroyWindow(clipboard_owner_);
clipboard_owner_ = NULL;
}
void Clipboard::WriteObjects(const ObjectMap& objects) {
WriteObjects(objects, NULL);
}
void Clipboard::WriteObjects(const ObjectMap& objects,
base::ProcessHandle process) {
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
::EmptyClipboard();
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
if (iter->first == CBF_SMBITMAP)
WriteBitmapFromSharedMemory(&(iter->second[0].front()),
&(iter->second[1].front()),
process);
else
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
string16 text;
UTF8ToUTF16(text_data, text_len, &text);
HGLOBAL glob = CreateGlobalData(text);
WriteToClipboard(CF_UNICODETEXT, glob);
}
void Clipboard::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
std::string markup(markup_data, markup_len);
std::string url;
if (url_len > 0)
url.assign(url_data, url_len);
std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
HGLOBAL glob = CreateGlobalData(html_fragment);
WriteToClipboard(StringToInt(GetHtmlFormatType()), glob);
}
void Clipboard::WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
std::string bookmark(title_data, title_len);
bookmark.append(1, L'\n');
bookmark.append(url_data, url_len);
string16 wide_bookmark = UTF8ToWide(bookmark);
HGLOBAL glob = CreateGlobalData(wide_bookmark);
WriteToClipboard(StringToInt(GetUrlWFormatType()), glob);
}
void Clipboard::WriteHyperlink(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
// Store as a bookmark.
WriteBookmark(title_data, title_len, url_data, url_len);
std::string title(title_data, title_len),
url(url_data, url_len),
link("<a href=\"");
// Construct the hyperlink.
link.append(url);
link.append("\">");
link.append(title);
link.append("</a>");
// Store hyperlink as html.
WriteHTML(link.c_str(), link.size(), NULL, 0);
}
void Clipboard::WriteWebSmartPaste() {
DCHECK(clipboard_owner_);
::SetClipboardData(StringToInt(GetWebKitSmartPasteFormatType()), NULL);
}
void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
HDC dc = ::GetDC(NULL);
// This doesn't actually cost us a memcpy when the bitmap comes from the
// renderer as we load it into the bitmap using setPixels which just sets a
// pointer. Someone has to memcpy it into GDI, it might as well be us here.
// TODO(darin): share data in gfx/bitmap_header.cc somehow
BITMAPINFO bm_info = {0};
bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm_info.bmiHeader.biWidth = size->width();
bm_info.bmiHeader.biHeight = -size->height(); // sets vertical orientation
bm_info.bmiHeader.biPlanes = 1;
bm_info.bmiHeader.biBitCount = 32;
bm_info.bmiHeader.biCompression = BI_RGB;
// ::CreateDIBSection allocates memory for us to copy our bitmap into.
// Unfortunately, we can't write the created bitmap to the clipboard,
// (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
void *bits;
HBITMAP source_hbitmap =
::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
if (bits && source_hbitmap) {
// Copy the bitmap out of shared memory and into GDI
memcpy(bits, pixel_data, 4 * size->width() * size->height());
// Now we have an HBITMAP, we can write it to the clipboard
WriteBitmapFromHandle(source_hbitmap, *size);
}
::DeleteObject(source_hbitmap);
::ReleaseDC(NULL, dc);
}
void Clipboard::WriteBitmapFromSharedMemory(const char* bitmap_data,
const char* size_data,
base::ProcessHandle process) {
const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
// bitmap_data has an encoded shared memory object. See
// DuplicateRemoteHandles().
char* ptr = const_cast<char*>(bitmap_data);
scoped_ptr<const base::SharedMemory> bitmap(*
reinterpret_cast<const base::SharedMemory**>(ptr));
// TODO(darin): share data in gfx/bitmap_header.cc somehow.
BITMAPINFO bm_info = {0};
bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm_info.bmiHeader.biWidth = size->width();
// Sets the vertical orientation.
bm_info.bmiHeader.biHeight = -size->height();
bm_info.bmiHeader.biPlanes = 1;
bm_info.bmiHeader.biBitCount = 32;
bm_info.bmiHeader.biCompression = BI_RGB;
HDC dc = ::GetDC(NULL);
// We can create an HBITMAP directly using the shared memory handle, saving
// a memcpy.
HBITMAP source_hbitmap =
::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL,
bitmap->handle(), 0);
if (source_hbitmap) {
// Now we can write the HBITMAP to the clipboard
WriteBitmapFromHandle(source_hbitmap, *size);
}
::DeleteObject(source_hbitmap);
::ReleaseDC(NULL, dc);
}
void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
const gfx::Size& size) {
// We would like to just call ::SetClipboardData on the source_hbitmap,
// but that bitmap might not be of a sort we can write to the clipboard.
// For this reason, we create a new bitmap, copy the bits over, and then
// write that to the clipboard.
HDC dc = ::GetDC(NULL);
HDC compatible_dc = ::CreateCompatibleDC(NULL);
HDC source_dc = ::CreateCompatibleDC(NULL);
// This is the HBITMAP we will eventually write to the clipboard
HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
if (!hbitmap) {
// Failed to create the bitmap
::DeleteDC(compatible_dc);
::DeleteDC(source_dc);
::ReleaseDC(NULL, dc);
return;
}
HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
// Now we need to blend it into an HBITMAP we can place on the clipboard
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
source_dc, 0, 0, size.width(), size.height(), bf);
// Clean up all the handles we just opened
::SelectObject(compatible_dc, old_hbitmap);
::SelectObject(source_dc, old_source);
::DeleteObject(old_hbitmap);
::DeleteObject(old_source);
::DeleteDC(compatible_dc);
::DeleteDC(source_dc);
::ReleaseDC(NULL, dc);
WriteToClipboard(CF_BITMAP, hbitmap);
}
// Write a file or set of files to the clipboard in HDROP format. When the user
// invokes a paste command (in a Windows explorer shell, for example), the files
// will be copied to the paste location.
void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
// Calculate the amount of space we'll need store the strings and
// a DROPFILES struct.
size_t bytes = sizeof(DROPFILES) + file_len;
HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
if (!hdata)
return;
char* data = static_cast<char*>(::GlobalLock(hdata));
DROPFILES* drop_files = reinterpret_cast<DROPFILES*>(data);
drop_files->pFiles = sizeof(DROPFILES);
drop_files->fWide = TRUE;
memcpy(data + sizeof(DROPFILES), file_data, file_len);
::GlobalUnlock(hdata);
WriteToClipboard(CF_HDROP, hdata);
}
void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) {
DCHECK(clipboard_owner_);
if (handle && !::SetClipboardData(format, handle)) {
DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
FreeData(format, handle);
}
}
bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const {
return ::IsClipboardFormatAvailable(StringToInt(format)) != FALSE;
}
void Clipboard::ReadText(string16* result) const {
if (!result) {
NOTREACHED();
return;
}
result->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
if (!data)
return;
result->assign(static_cast<const char16*>(::GlobalLock(data)));
::GlobalUnlock(data);
}
void Clipboard::ReadAsciiText(std::string* result) const {
if (!result) {
NOTREACHED();
return;
}
result->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(CF_TEXT);
if (!data)
return;
result->assign(static_cast<const char*>(::GlobalLock(data)));
::GlobalUnlock(data);
}
void Clipboard::ReadHTML(string16* markup, std::string* src_url) const {
if (markup)
markup->clear();
if (src_url)
src_url->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(StringToInt(GetHtmlFormatType()));
if (!data)
return;
std::string html_fragment(static_cast<const char*>(::GlobalLock(data)));
::GlobalUnlock(data);
std::string markup_utf8;
ClipboardUtil::CFHtmlToHtml(html_fragment, &markup_utf8, src_url);
markup->assign(UTF8ToWide(markup_utf8));
}
void Clipboard::ReadBookmark(string16* title, std::string* url) const {
if (title)
title->clear();
if (url)
url->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(StringToInt(GetUrlWFormatType()));
if (!data)
return;
string16 bookmark(static_cast<const char16*>(::GlobalLock(data)));
::GlobalUnlock(data);
ParseBookmarkClipboardFormat(bookmark, title, url);
}
// Read a file in HDROP format from the clipboard.
void Clipboard::ReadFile(FilePath* file) const {
if (!file) {
NOTREACHED();
return;
}
*file = FilePath();
std::vector<FilePath> files;
ReadFiles(&files);
// Take the first file, if available.
if (!files.empty())
*file = files[0];
}
// Read a set of files in HDROP format from the clipboard.
void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
if (!files) {
NOTREACHED();
return;
}
files->clear();
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HDROP drop = static_cast<HDROP>(::GetClipboardData(CF_HDROP));
if (!drop)
return;
// Count of files in the HDROP.
int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0);
if (count) {
for (int i = 0; i < count; ++i) {
int size = ::DragQueryFile(drop, i, NULL, 0) + 1;
std::wstring file;
::DragQueryFile(drop, i, WriteInto(&file, size), size);
files->push_back(FilePath(file));
}
}
}
// static
void Clipboard::ParseBookmarkClipboardFormat(const string16& bookmark,
string16* title,
std::string* url) {
const string16 kDelim = ASCIIToUTF16("\r\n");
const size_t title_end = bookmark.find_first_of(kDelim);
if (title)
title->assign(bookmark.substr(0, title_end));
if (url) {
const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
if (url_start != string16::npos)
*url = UTF16ToUTF8(bookmark.substr(url_start, string16::npos));
}
}
// static
Clipboard::FormatType Clipboard::GetUrlFormatType() {
return IntToString(ClipboardUtil::GetUrlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetUrlWFormatType() {
return IntToString(ClipboardUtil::GetUrlWFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetMozUrlFormatType() {
return IntToString(ClipboardUtil::GetMozUrlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
return IntToString(ClipboardUtil::GetPlainTextFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
return IntToString(ClipboardUtil::GetPlainTextWFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFilenameFormatType() {
return IntToString(ClipboardUtil::GetFilenameFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
return IntToString(ClipboardUtil::GetFilenameWFormat()->cfFormat);
}
// MS HTML Format
// static
Clipboard::FormatType Clipboard::GetHtmlFormatType() {
return IntToString(ClipboardUtil::GetHtmlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetBitmapFormatType() {
return IntToString(CF_BITMAP);
}
// Firefox text/html
// static
Clipboard::FormatType Clipboard::GetTextHtmlFormatType() {
return IntToString(ClipboardUtil::GetTextHtmlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetCFHDropFormatType() {
return IntToString(ClipboardUtil::GetCFHDropFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFileDescriptorFormatType() {
return IntToString(ClipboardUtil::GetFileDescriptorFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() {
return IntToString(ClipboardUtil::GetFileContentFormatZero()->cfFormat);
}
// static
void Clipboard::DuplicateRemoteHandles(base::ProcessHandle process,
ObjectMap* objects) {
for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
++iter) {
if (iter->first == CBF_SMBITMAP) {
// There is a shared memory handle encoded on the first ObjectMapParam.
// Use it to open a local handle to the memory.
char* bitmap_data = &(iter->second[0].front());
base::SharedMemoryHandle* remote_bitmap_handle =
reinterpret_cast<base::SharedMemoryHandle*>(bitmap_data);
base::SharedMemory* bitmap = new base::SharedMemory(*remote_bitmap_handle,
false, process);
// We store the object where the remote handle was located so it can
// be retrieved by the UI thread (see WriteBitmapFromSharedMemory()).
iter->second[0].clear();
for (size_t i = 0; i < sizeof(bitmap); i++)
iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
}
}
}
// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
return IntToString(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat);
}
// static
void Clipboard::FreeData(unsigned int format, HANDLE data) {
if (format == CF_BITMAP)
::DeleteObject(static_cast<HBITMAP>(data));
else
::GlobalFree(data);
}
HWND Clipboard::GetClipboardWindow() const {
if (!clipboard_owner_ && create_window_) {
clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass",
L"ClipboardOwnerWindow",
0, 0, 0, 0, 0,
HWND_MESSAGE,
0, 0, 0);
}
return clipboard_owner_;
}

Просмотреть файл

@ -0,0 +1,362 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#if defined(OS_WIN)
#include <windows.h>
#include <shellapi.h>
#endif
#include <algorithm>
#include "base/logging.h"
#include "base/singleton.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
CommandLine* CommandLine::current_process_commandline_ = NULL;
// Since we use a lazy match, make sure that longer versions (like L"--")
// are listed before shorter versions (like L"-") of similar prefixes.
#if defined(OS_WIN)
const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
const wchar_t kSwitchTerminator[] = L"--";
const wchar_t kSwitchValueSeparator[] = L"=";
#elif defined(OS_POSIX)
// Unixes don't use slash as a switch.
const char* const kSwitchPrefixes[] = {"--", "-"};
const char kSwitchTerminator[] = "--";
const char kSwitchValueSeparator[] = "=";
#endif
#if defined(OS_WIN)
// Lowercase a string. This is used to lowercase switch names.
// Is this what we really want? It seems crazy to me. I've left it in
// for backwards compatibility on Windows.
static void Lowercase(std::wstring* parameter) {
transform(parameter->begin(), parameter->end(), parameter->begin(),
tolower);
}
#endif
#if defined(OS_WIN)
void CommandLine::ParseFromString(const std::wstring& command_line) {
TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
if (command_line_string_.empty())
return;
int num_args = 0;
wchar_t** args = NULL;
args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
// Populate program_ with the trimmed version of the first arg.
TrimWhitespace(args[0], TRIM_ALL, &program_);
bool parse_switches = true;
for (int i = 1; i < num_args; ++i) {
std::wstring arg;
TrimWhitespace(args[i], TRIM_ALL, &arg);
if (!parse_switches) {
loose_values_.push_back(arg);
continue;
}
if (arg == kSwitchTerminator) {
parse_switches = false;
continue;
}
std::string switch_string;
std::wstring switch_value;
if (IsSwitch(arg, &switch_string, &switch_value)) {
switches_[switch_string] = switch_value;
} else {
loose_values_.push_back(arg);
}
}
if (args)
LocalFree(args);
}
CommandLine::CommandLine(const std::wstring& program) {
if (!program.empty()) {
program_ = program;
command_line_string_ = L'"' + program + L'"';
}
}
#elif defined(OS_POSIX)
CommandLine::CommandLine(int argc, const char* const* argv) {
for (int i = 0; i < argc; ++i)
argv_.push_back(argv[i]);
InitFromArgv();
}
CommandLine::CommandLine(const std::vector<std::string>& argv) {
argv_ = argv;
InitFromArgv();
}
void CommandLine::InitFromArgv() {
bool parse_switches = true;
for (size_t i = 1; i < argv_.size(); ++i) {
const std::string& arg = argv_[i];
if (!parse_switches) {
loose_values_.push_back(arg);
continue;
}
if (arg == kSwitchTerminator) {
parse_switches = false;
continue;
}
std::string switch_string;
std::string switch_value;
if (IsSwitch(arg, &switch_string, &switch_value)) {
switches_[switch_string] = switch_value;
} else {
loose_values_.push_back(arg);
}
}
}
CommandLine::CommandLine(const std::wstring& program) {
argv_.push_back(WideToASCII(program));
}
#endif
// static
bool CommandLine::IsSwitch(const StringType& parameter_string,
std::string* switch_string,
StringType* switch_value) {
switch_string->clear();
switch_value->clear();
for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
StringType prefix(kSwitchPrefixes[i]);
if (parameter_string.find(prefix) != 0)
continue;
const size_t switch_start = prefix.length();
const size_t equals_position = parameter_string.find(
kSwitchValueSeparator, switch_start);
StringType switch_native;
if (equals_position == StringType::npos) {
switch_native = parameter_string.substr(switch_start);
} else {
switch_native = parameter_string.substr(
switch_start, equals_position - switch_start);
*switch_value = parameter_string.substr(equals_position + 1);
}
#if defined(OS_WIN)
Lowercase(&switch_native);
*switch_string = WideToASCII(switch_native);
#else
*switch_string = switch_native;
#endif
return true;
}
return false;
}
// static
void CommandLine::Init(int argc, const char* const* argv) {
DCHECK(current_process_commandline_ == NULL);
#if defined(OS_WIN)
current_process_commandline_ = new CommandLine;
current_process_commandline_->ParseFromString(::GetCommandLineW());
#elif defined(OS_POSIX)
current_process_commandline_ = new CommandLine(argc, argv);
#endif
}
void CommandLine::Terminate() {
DCHECK(current_process_commandline_ != NULL);
delete current_process_commandline_;
current_process_commandline_ = NULL;
}
bool CommandLine::HasSwitch(const std::wstring& switch_string) const {
std::wstring lowercased_switch(switch_string);
#if defined(OS_WIN)
Lowercase(&lowercased_switch);
#endif
return switches_.find(WideToASCII(lowercased_switch)) != switches_.end();
}
std::wstring CommandLine::GetSwitchValue(
const std::wstring& switch_string) const {
std::wstring lowercased_switch(switch_string);
#if defined(OS_WIN)
Lowercase(&lowercased_switch);
#endif
std::map<std::string, StringType>::const_iterator result =
switches_.find(WideToASCII(lowercased_switch));
if (result == switches_.end()) {
return L"";
} else {
#if defined(OS_WIN)
return result->second;
#else
return ASCIIToWide(result->second);
#endif
}
}
#if defined(OS_WIN)
std::vector<std::wstring> CommandLine::GetLooseValues() const {
return loose_values_;
}
std::wstring CommandLine::program() const {
return program_;
}
#else
std::vector<std::wstring> CommandLine::GetLooseValues() const {
std::vector<std::wstring> values;
for (size_t i = 0; i < loose_values_.size(); ++i)
values.push_back(ASCIIToWide(loose_values_[i]));
return values;
}
std::wstring CommandLine::program() const {
DCHECK(argv_.size() > 0);
return ASCIIToWide(argv_[0]);
}
#endif
// static
std::wstring CommandLine::PrefixedSwitchString(
const std::wstring& switch_string) {
return StringPrintf(L"%ls%ls",
kSwitchPrefixes[0],
switch_string.c_str());
}
// static
std::wstring CommandLine::PrefixedSwitchStringWithValue(
const std::wstring& switch_string, const std::wstring& value_string) {
if (value_string.empty()) {
return PrefixedSwitchString(switch_string);
}
return StringPrintf(L"%ls%ls%ls%ls",
kSwitchPrefixes[0],
switch_string.c_str(),
kSwitchValueSeparator,
value_string.c_str());
}
#if defined(OS_WIN)
void CommandLine::AppendSwitch(const std::wstring& switch_string) {
std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
command_line_string_.append(L" ");
command_line_string_.append(prefixed_switch_string);
switches_[WideToASCII(switch_string)] = L"";
}
void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
const std::wstring& value_string) {
std::wstring value_string_edit;
// NOTE(jhughes): If the value contains a quotation mark at one
// end but not both, you may get unusable output.
if (!value_string.empty() &&
(value_string.find(L" ") != std::wstring::npos) &&
(value_string[0] != L'"') &&
(value_string[value_string.length() - 1] != L'"')) {
// need to provide quotes
value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str());
} else {
value_string_edit = value_string;
}
std::wstring combined_switch_string =
PrefixedSwitchStringWithValue(switch_string, value_string_edit);
command_line_string_.append(L" ");
command_line_string_.append(combined_switch_string);
switches_[WideToASCII(switch_string)] = value_string;
}
void CommandLine::AppendLooseValue(const std::wstring& value) {
// TODO(evan): quoting?
command_line_string_.append(L" ");
command_line_string_.append(value);
}
void CommandLine::AppendArguments(const CommandLine& other,
bool include_program) {
// Verify include_program is used correctly.
// Logic could be shorter but this is clearer.
DCHECK(include_program ? !other.program().empty() : other.program().empty());
command_line_string_ += L" " + 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;
}
void CommandLine::PrependWrapper(const std::wstring& wrapper) {
// The wrapper may have embedded arguments (like "gdb --args"). In this case,
// we don't pretend to do anything fancy, we just split on spaces.
std::vector<std::wstring> wrapper_and_args;
SplitString(wrapper, ' ', &wrapper_and_args);
program_ = wrapper_and_args[0];
command_line_string_ = wrapper + L" " + command_line_string_;
}
#elif defined(OS_POSIX)
void CommandLine::AppendSwitch(const std::wstring& switch_string) {
std::string ascii_switch = WideToASCII(switch_string);
argv_.push_back(kSwitchPrefixes[0] + ascii_switch);
switches_[ascii_switch] = "";
}
void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
const std::wstring& value_string) {
std::string ascii_switch = WideToASCII(switch_string);
std::string ascii_value = WideToASCII(value_string);
argv_.push_back(kSwitchPrefixes[0] + ascii_switch +
kSwitchValueSeparator + ascii_value);
switches_[ascii_switch] = ascii_value;
}
void CommandLine::AppendLooseValue(const std::wstring& value) {
argv_.push_back(WideToASCII(value));
}
void CommandLine::AppendArguments(const CommandLine& other,
bool include_program) {
// Verify include_program is used correctly.
// Logic could be shorter but this is clearer.
DCHECK(include_program ? !other.program().empty() : other.program().empty());
size_t first_arg = include_program ? 0 : 1;
for (size_t i = first_arg; 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;
}
void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) {
// The wrapper may have embedded arguments (like "gdb --args"). In this case,
// we don't pretend to do anything fancy, we just split on spaces.
const std::string wrapper = WideToASCII(wrapper_wide);
std::vector<std::string> wrapper_and_args;
SplitString(wrapper, ' ', &wrapper_and_args);
argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
}
#endif

Просмотреть файл

@ -0,0 +1,185 @@
// Copyright (c) 2006-2008 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 class works with command lines: building and parsing.
// Switches can optionally have a value attached using an equals sign,
// as in "-switch=value". Arguments that aren't prefixed with a
// switch prefix are considered "loose parameters". Switch names are
// case-insensitive. An argument of "--" will terminate switch
// parsing, causing everything after to be considered as loose
// parameters.
// There is a singleton read-only CommandLine that represents the command
// line that the current process was started with. It must be initialized
// in main() (or whatever the platform's equivalent function is).
#ifndef BASE_COMMAND_LINE_H_
#define BASE_COMMAND_LINE_H_
#include "build/build_config.h"
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
class InProcessBrowserTest;
class CommandLine {
public:
#if defined(OS_WIN)
// Creates a parsed version of the given command-line string.
// The program name is assumed to be the first item in the string.
void ParseFromString(const std::wstring& command_line);
#elif defined(OS_POSIX)
// Initialize from an argv vector (or directly from main()'s argv).
CommandLine(int argc, const char* const* argv);
explicit CommandLine(const std::vector<std::string>& argv);
#endif
// Construct a new, empty command line.
// |program| is the name of the program to run (aka argv[0]).
// TODO(port): should be a FilePath.
explicit CommandLine(const std::wstring& program);
// Initialize the current process CommandLine singleton. On Windows,
// ignores its arguments (we instead parse GetCommandLineW()
// directly) because we don't trust the CRT's parsing of the command
// line, but it still must be called to set up the command line.
static void Init(int argc, const char* const* argv);
// Destroys the current process CommandLine singleton. This is necessary if
// you want to reset the base library to its initial state (for example in an
// outer library that needs to be able to terminate, and be re-initialized).
// If Init is called only once, e.g. in main(), calling Terminate() is not
// necessary.
static void Terminate();
// Get the singleton CommandLine representing the current process's
// command line.
static const CommandLine* ForCurrentProcess() {
DCHECK(current_process_commandline_);
return current_process_commandline_;
}
// Returns true if this command line contains the given switch.
// (Switch names are case-insensitive.)
bool HasSwitch(const std::wstring& switch_string) const;
// Returns the value associated with the given switch. If the
// switch has no value or isn't present, this method returns
// the empty string.
std::wstring GetSwitchValue(const std::wstring& switch_string) const;
// Get the remaining arguments to the command.
// WARNING: this is incorrect on POSIX; we must do string conversions.
std::vector<std::wstring> GetLooseValues() const;
#if defined(OS_WIN)
// Returns the original command line string.
const std::wstring& command_line_string() const {
return command_line_string_;
}
#elif defined(OS_POSIX)
// Returns the original command line string as a vector of strings.
const std::vector<std::string>& argv() const {
return argv_;
}
#endif
// Returns the program part of the command line string (the first item).
std::wstring program() const;
// Return a copy of the string prefixed with a switch prefix.
// Used internally.
static std::wstring PrefixedSwitchString(const std::wstring& switch_string);
// Return a copy of the string prefixed with a switch prefix,
// and appended with the given value. Used internally.
static std::wstring PrefixedSwitchStringWithValue(
const std::wstring& switch_string,
const std::wstring& value_string);
// Appends the given switch string (preceded by a space and a switch
// prefix) to the given string.
void AppendSwitch(const std::wstring& switch_string);
// Appends the given switch string (preceded by a space and a switch
// prefix) to the given string, with the given value attached.
void AppendSwitchWithValue(const std::wstring& switch_string,
const std::wstring& value_string);
// Append a loose value to the command line.
void AppendLooseValue(const std::wstring& value);
// Append the arguments from another command line to this one.
// If |include_program| is true, include |other|'s program as well.
void AppendArguments(const CommandLine& other,
bool include_program);
// On POSIX systems it's common to run processes via a wrapper (like
// "valgrind" or "gdb --args").
void PrependWrapper(const std::wstring& wrapper);
private:
friend class InProcessBrowserTest;
CommandLine() {}
// Used by InProcessBrowserTest.
static CommandLine* ForCurrentProcessMutable() {
DCHECK(current_process_commandline_);
return current_process_commandline_;
}
// The singleton CommandLine instance representing the current process's
// command line.
static CommandLine* current_process_commandline_;
// We store a platform-native version of the command line, used when building
// up a new command line to be executed. This ifdef delimits that code.
#if defined(OS_WIN)
// The quoted, space-separated command-line string.
std::wstring command_line_string_;
// The name of the program.
std::wstring program_;
// The type of native command line arguments.
typedef std::wstring StringType;
#elif defined(OS_POSIX)
// The argv array, with the program name in argv_[0].
std::vector<std::string> argv_;
// The type of native command line arguments.
typedef std::string StringType;
// Shared by the two POSIX constructor forms. Initalize from argv_.
void InitFromArgv();
#endif
// Returns true and fills in |switch_string| and |switch_value|
// if |parameter_string| represents a switch.
static bool IsSwitch(const StringType& parameter_string,
std::string* switch_string,
StringType* switch_value);
// Parsed-out values.
std::map<std::string, StringType> switches_;
// Non-switch command-line arguments.
std::vector<StringType> loose_values_;
// We allow copy constructors, because a common pattern is to grab a
// copy of the current process's command line and then add some
// flags to it. E.g.:
// CommandLine cl(*CommandLine::ForCurrentProcess());
// cl.AppendSwitch(...);
};
#endif // BASE_COMMAND_LINE_H_

Просмотреть файл

@ -0,0 +1,128 @@
// Copyright (c) 2006-2008 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 <string>
#include <vector>
#include "base/command_line.h"
#include "base/basictypes.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(CommandLineTest, CommandLineConstructor) {
#if defined(OS_WIN)
CommandLine cl(L"");
cl.ParseFromString(L"program --foo= -bAr /Spaetzel=pierogi /Baz flim "
L"--other-switches=\"--dog=canine --cat=feline\" "
L"-spaetzle=Crepe -=loosevalue flan "
L"--input-translation=\"45\"--output-rotation "
L"-- -- --not-a-switch "
L"\"in the time of submarines...\"");
EXPECT_FALSE(cl.command_line_string().empty());
#elif defined(OS_POSIX)
const char* argv[] = {"program", "--foo=", "-bar",
"-spaetzel=pierogi", "-baz", "flim",
"--other-switches=--dog=canine --cat=feline",
"-spaetzle=Crepe", "-=loosevalue", "flan",
"--input-translation=45--output-rotation",
"--", "--", "--not-a-switch",
"in the time of submarines..."};
CommandLine cl(arraysize(argv), argv);
#endif
EXPECT_FALSE(cl.HasSwitch(L"cruller"));
EXPECT_FALSE(cl.HasSwitch(L"flim"));
EXPECT_FALSE(cl.HasSwitch(L"program"));
EXPECT_FALSE(cl.HasSwitch(L"dog"));
EXPECT_FALSE(cl.HasSwitch(L"cat"));
EXPECT_FALSE(cl.HasSwitch(L"output-rotation"));
EXPECT_FALSE(cl.HasSwitch(L"not-a-switch"));
EXPECT_FALSE(cl.HasSwitch(L"--"));
EXPECT_EQ(L"program", cl.program());
EXPECT_TRUE(cl.HasSwitch(L"foo"));
EXPECT_TRUE(cl.HasSwitch(L"bar"));
EXPECT_TRUE(cl.HasSwitch(L"baz"));
EXPECT_TRUE(cl.HasSwitch(L"spaetzle"));
#if defined(OS_WIN)
EXPECT_TRUE(cl.HasSwitch(L"SPAETZLE"));
#endif
EXPECT_TRUE(cl.HasSwitch(L"other-switches"));
EXPECT_TRUE(cl.HasSwitch(L"input-translation"));
EXPECT_EQ(L"Crepe", cl.GetSwitchValue(L"spaetzle"));
EXPECT_EQ(L"", cl.GetSwitchValue(L"Foo"));
EXPECT_EQ(L"", cl.GetSwitchValue(L"bar"));
EXPECT_EQ(L"", cl.GetSwitchValue(L"cruller"));
EXPECT_EQ(L"--dog=canine --cat=feline", cl.GetSwitchValue(L"other-switches"));
EXPECT_EQ(L"45--output-rotation", cl.GetSwitchValue(L"input-translation"));
std::vector<std::wstring> loose_values = cl.GetLooseValues();
ASSERT_EQ(5U, loose_values.size());
std::vector<std::wstring>::const_iterator iter = loose_values.begin();
EXPECT_EQ(L"flim", *iter);
++iter;
EXPECT_EQ(L"flan", *iter);
++iter;
EXPECT_EQ(L"--", *iter);
++iter;
EXPECT_EQ(L"--not-a-switch", *iter);
++iter;
EXPECT_EQ(L"in the time of submarines...", *iter);
++iter;
EXPECT_TRUE(iter == loose_values.end());
#if defined(OS_POSIX)
const std::vector<std::string>& argvec = cl.argv();
for (size_t i = 0; i < argvec.size(); i++) {
EXPECT_EQ(0, argvec[i].compare(argv[i]));
}
#endif
}
// Tests behavior with an empty input string.
TEST(CommandLineTest, EmptyString) {
#if defined(OS_WIN)
CommandLine cl(L"");
EXPECT_TRUE(cl.command_line_string().empty());
EXPECT_TRUE(cl.program().empty());
#elif defined(OS_POSIX)
CommandLine cl(0, NULL);
EXPECT_TRUE(cl.argv().size() == 0);
#endif
EXPECT_EQ(0U, cl.GetLooseValues().size());
}
// Test methods for appending switches to a command line.
TEST(CommandLineTest, AppendSwitches) {
std::wstring switch1 = L"switch1";
std::wstring switch2 = L"switch2";
std::wstring value = L"value";
std::wstring switch3 = L"switch3";
std::wstring value3 = L"a value with spaces";
std::wstring switch4 = L"switch4";
std::wstring value4 = L"\"a value with quotes\"";
#if defined(OS_WIN)
CommandLine cl(L"Program");
#elif defined(OS_POSIX)
std::vector<std::string> argv;
argv.push_back(std::string("Program"));
CommandLine cl(argv);
#endif
cl.AppendSwitch(switch1);
cl.AppendSwitchWithValue(switch2, value);
cl.AppendSwitchWithValue(switch3, value3);
cl.AppendSwitchWithValue(switch4, value4);
EXPECT_TRUE(cl.HasSwitch(switch1));
EXPECT_TRUE(cl.HasSwitch(switch2));
EXPECT_EQ(value, cl.GetSwitchValue(switch2));
EXPECT_TRUE(cl.HasSwitch(switch3));
EXPECT_EQ(value3, cl.GetSwitchValue(switch3));
EXPECT_TRUE(cl.HasSwitch(switch4));
EXPECT_EQ(value4, cl.GetSwitchValue(switch4));
}

Просмотреть файл

@ -0,0 +1,77 @@
// Copyright (c) 2006-2008 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_COMPILER_SPECIFIC_H_
#define BASE_COMPILER_SPECIFIC_H_
#include "build/build_config.h"
#if defined(COMPILER_MSVC)
// Macros for suppressing and disabling warnings on MSVC.
//
// Warning numbers are enumerated at:
// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
//
// The warning pragma:
// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
//
// Using __pragma instead of #pragma inside macros:
// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
// for the next line of the source file.
#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n))
// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
// The warning remains disabled until popped by MSVC_POP_WARNING.
#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
__pragma(warning(disable:n))
// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level
// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all
// warnings.
#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
// Pop effects of innermost MSVC_PUSH_* macro.
#define MSVC_POP_WARNING() __pragma(warning(pop))
#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off))
#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on))
// Allows |this| to be passed as an argument in constructor initializer lists.
// This uses push/pop instead of the seemingly simpler suppress feature to avoid
// having the warning be disabled for more than just |code|.
//
// Example usage:
// Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {}
//
// Compiler warning C4355: 'this': used in base member initializer list:
// http://msdn.microsoft.com/en-us/library/3c594ae3(VS.80).aspx
#define ALLOW_THIS_IN_INITIALIZER_LIST(code) MSVC_PUSH_DISABLE_WARNING(4355) \
code \
MSVC_POP_WARNING()
#else // Not MSVC
#define MSVC_SUPPRESS_WARNING(n)
#define MSVC_PUSH_DISABLE_WARNING(n)
#define MSVC_PUSH_WARNING_LEVEL(n)
#define MSVC_POP_WARNING()
#define MSVC_DISABLE_OPTIMIZE()
#define MSVC_ENABLE_OPTIMIZE()
#define ALLOW_THIS_IN_INITIALIZER_LIST(code) code
#endif // COMPILER_MSVC
#if defined(COMPILER_GCC)
#define ALLOW_UNUSED __attribute__((unused))
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else // Not GCC
#define ALLOW_UNUSED
#define WARN_UNUSED_RESULT
#endif
#endif // BASE_COMPILER_SPECIFIC_H_

Просмотреть файл

@ -0,0 +1,174 @@
// Copyright (c) 2006-2008 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.
// ConditionVariable wraps pthreads condition variable synchronization or, on
// Windows, simulates it. This functionality is very helpful for having
// several threads wait for an event, as is common with a thread pool managed
// by a master. The meaning of such an event in the (worker) thread pool
// scenario is that additional tasks are now available for processing. It is
// used in Chrome in the DNS prefetching system to notify worker threads that
// a queue now has items (tasks) which need to be tended to. A related use
// would have a pool manager waiting on a ConditionVariable, waiting for a
// thread in the pool to announce (signal) that there is now more room in a
// (bounded size) communications queue for the manager to deposit tasks, or,
// as a second example, that the queue of tasks is completely empty and all
// workers are waiting.
//
// USAGE NOTE 1: spurious signal events are possible with this and
// most implementations of condition variables. As a result, be
// *sure* to retest your condition before proceeding. The following
// is a good example of doing this correctly:
//
// while (!work_to_be_done()) Wait(...);
//
// In contrast do NOT do the following:
//
// if (!work_to_be_done()) Wait(...); // Don't do this.
//
// Especially avoid the above if you are relying on some other thread only
// issuing a signal up *if* there is work-to-do. There can/will
// be spurious signals. Recheck state on waiting thread before
// assuming the signal was intentional. Caveat caller ;-).
//
// USAGE NOTE 2: Broadcast() frees up all waiting threads at once,
// which leads to contention for the locks they all held when they
// called Wait(). This results in POOR performance. A much better
// approach to getting a lot of threads out of Wait() is to have each
// thread (upon exiting Wait()) call Signal() to free up another
// Wait'ing thread. Look at condition_variable_unittest.cc for
// both examples.
//
// Broadcast() can be used nicely during teardown, as it gets the job
// done, and leaves no sleeping threads... and performance is less
// critical at that point.
//
// The semantics of Broadcast() are carefully crafted so that *all*
// threads that were waiting when the request was made will indeed
// get signaled. Some implementations mess up, and don't signal them
// all, while others allow the wait to be effectively turned off (for
// a while while waiting threads come around). This implementation
// appears correct, as it will not "lose" any signals, and will guarantee
// that all threads get signaled by Broadcast().
//
// This implementation offers support for "performance" in its selection of
// which thread to revive. Performance, in direct contrast with "fairness,"
// assures that the thread that most recently began to Wait() is selected by
// Signal to revive. Fairness would (if publicly supported) assure that the
// thread that has Wait()ed the longest is selected. The default policy
// may improve performance, as the selected thread may have a greater chance of
// having some of its stack data in various CPU caches.
//
// For a discussion of the many very subtle implementation details, see the FAQ
// at the end of condition_variable_win.cc.
#ifndef BASE_CONDITION_VARIABLE_H_
#define BASE_CONDITION_VARIABLE_H_
#include "base/lock.h"
namespace base {
class TimeDelta;
}
class ConditionVariable {
public:
// Construct a cv for use with ONLY one user lock.
explicit ConditionVariable(Lock* user_lock);
~ConditionVariable();
// Wait() releases the caller's critical section atomically as it starts to
// sleep, and the reacquires it when it is signaled.
void Wait();
void TimedWait(const base::TimeDelta& max_time);
// Broadcast() revives all waiting threads.
void Broadcast();
// Signal() revives one waiting thread.
void Signal();
private:
#if defined(OS_WIN)
// Define Event class that is used to form circularly linked lists.
// The list container is an element with NULL as its handle_ value.
// The actual list elements have a non-zero handle_ value.
// All calls to methods MUST be done under protection of a lock so that links
// can be validated. Without the lock, some links might asynchronously
// change, and the assertions would fail (as would list change operations).
class Event {
public:
// Default constructor with no arguments creates a list container.
Event();
~Event();
// InitListElement transitions an instance from a container, to an element.
void InitListElement();
// Methods for use on lists.
bool IsEmpty() const;
void PushBack(Event* other);
Event* PopFront();
Event* PopBack();
// Methods for use on list elements.
// Accessor method.
HANDLE handle() const;
// Pull an element from a list (if it's in one).
Event* Extract();
// Method for use on a list element or on a list.
bool IsSingleton() const;
private:
// Provide pre/post conditions to validate correct manipulations.
bool ValidateAsDistinct(Event* other) const;
bool ValidateAsItem() const;
bool ValidateAsList() const;
bool ValidateLinks() const;
HANDLE handle_;
Event* next_;
Event* prev_;
DISALLOW_COPY_AND_ASSIGN(Event);
};
// Note that RUNNING is an unlikely number to have in RAM by accident.
// This helps with defensive destructor coding in the face of user error.
enum RunState { SHUTDOWN = 0, RUNNING = 64213 };
// Internal implementation methods supporting Wait().
Event* GetEventForWaiting();
void RecycleEvent(Event* used_event);
RunState run_state_;
// Private critical section for access to member data.
Lock internal_lock_;
// Lock that is acquired before calling Wait().
Lock& user_lock_;
// Events that threads are blocked on.
Event waiting_list_;
// Free list for old events.
Event recycling_list_;
int recycling_list_size_;
// The number of allocated, but not yet deleted events.
int allocation_counter_;
#elif defined(OS_POSIX)
pthread_cond_t condition_;
pthread_mutex_t* user_mutex_;
#endif
DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
};
#endif // BASE_CONDITION_VARIABLE_H_

Просмотреть файл

@ -0,0 +1,61 @@
// Copyright (c) 2006-2008 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/condition_variable.h"
#include <errno.h>
#include <sys/time.h>
#include "base/lock.h"
#include "base/lock_impl.h"
#include "base/logging.h"
#include "base/time.h"
using base::Time;
using base::TimeDelta;
ConditionVariable::ConditionVariable(Lock* user_lock)
: user_mutex_(user_lock->lock_impl()->os_lock()) {
int rv = pthread_cond_init(&condition_, NULL);
DCHECK(rv == 0);
}
ConditionVariable::~ConditionVariable() {
int rv = pthread_cond_destroy(&condition_);
DCHECK(rv == 0);
}
void ConditionVariable::Wait() {
int rv = pthread_cond_wait(&condition_, user_mutex_);
DCHECK(rv == 0);
}
void ConditionVariable::TimedWait(const TimeDelta& max_time) {
int64 usecs = max_time.InMicroseconds();
// The timeout argument to pthread_cond_timedwait is in absolute time.
struct timeval now;
gettimeofday(&now, NULL);
struct timespec abstime;
abstime.tv_sec = now.tv_sec + (usecs / Time::kMicrosecondsPerSecond);
abstime.tv_nsec = (now.tv_usec + (usecs % Time::kMicrosecondsPerSecond)) *
Time::kNanosecondsPerMicrosecond;
abstime.tv_sec += abstime.tv_nsec / Time::kNanosecondsPerSecond;
abstime.tv_nsec %= Time::kNanosecondsPerSecond;
DCHECK(abstime.tv_sec >= now.tv_sec); // Overflow paranoia
int rv = pthread_cond_timedwait(&condition_, user_mutex_, &abstime);
DCHECK(rv == 0 || rv == ETIMEDOUT);
}
void ConditionVariable::Broadcast() {
int rv = pthread_cond_broadcast(&condition_);
DCHECK(rv == 0);
}
void ConditionVariable::Signal() {
int rv = pthread_cond_signal(&condition_);
DCHECK(rv == 0);
}

Просмотреть файл

@ -0,0 +1,738 @@
// Copyright (c) 2006-2008 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.
// Multi-threaded tests of ConditionVariable class.
#include <time.h>
#include <algorithm>
#include <vector>
#include "base/condition_variable.h"
#include "base/logging.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/spin_wait.h"
#include "base/thread_collision_warner.h"
#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
using base::TimeDelta;
using base::TimeTicks;
namespace {
//------------------------------------------------------------------------------
// Define our test class, with several common variables.
//------------------------------------------------------------------------------
class ConditionVariableTest : public PlatformTest {
public:
const TimeDelta kZeroMs;
const TimeDelta kTenMs;
const TimeDelta kThirtyMs;
const TimeDelta kFortyFiveMs;
const TimeDelta kSixtyMs;
const TimeDelta kOneHundredMs;
explicit ConditionVariableTest()
: kZeroMs(TimeDelta::FromMilliseconds(0)),
kTenMs(TimeDelta::FromMilliseconds(10)),
kThirtyMs(TimeDelta::FromMilliseconds(30)),
kFortyFiveMs(TimeDelta::FromMilliseconds(45)),
kSixtyMs(TimeDelta::FromMilliseconds(60)),
kOneHundredMs(TimeDelta::FromMilliseconds(100)) {
}
};
//------------------------------------------------------------------------------
// Define a class that will control activities an several multi-threaded tests.
// The general structure of multi-threaded tests is that a test case will
// construct an instance of a WorkQueue. The WorkQueue will spin up some
// threads and control them throughout their lifetime, as well as maintaining
// a central repository of the work thread's activity. Finally, the WorkQueue
// will command the the worker threads to terminate. At that point, the test
// cases will validate that the WorkQueue has records showing that the desired
// activities were performed.
//------------------------------------------------------------------------------
// Callers are responsible for synchronizing access to the following class.
// The WorkQueue::lock_, as accessed via WorkQueue::lock(), should be used for
// all synchronized access.
class WorkQueue : public PlatformThread::Delegate {
public:
explicit WorkQueue(int thread_count);
~WorkQueue();
// PlatformThread::Delegate interface.
void ThreadMain();
//----------------------------------------------------------------------------
// Worker threads only call the following methods.
// They should use the lock to get exclusive access.
int GetThreadId(); // Get an ID assigned to a thread..
bool EveryIdWasAllocated() const; // Indicates that all IDs were handed out.
TimeDelta GetAnAssignment(int thread_id); // Get a work task duration.
void WorkIsCompleted(int thread_id);
int task_count() const;
bool allow_help_requests() const; // Workers can signal more workers.
bool shutdown() const; // Check if shutdown has been requested.
void thread_shutting_down();
//----------------------------------------------------------------------------
// Worker threads can call them but not needed to acquire a lock.
Lock* lock();
ConditionVariable* work_is_available();
ConditionVariable* all_threads_have_ids();
ConditionVariable* no_more_tasks();
//----------------------------------------------------------------------------
// The rest of the methods are for use by the controlling master thread (the
// test case code).
void ResetHistory();
int GetMinCompletionsByWorkerThread() const;
int GetMaxCompletionsByWorkerThread() const;
int GetNumThreadsTakingAssignments() const;
int GetNumThreadsCompletingTasks() const;
int GetNumberOfCompletedTasks() const;
TimeDelta GetWorkTime() const;
void SetWorkTime(TimeDelta delay);
void SetTaskCount(int count);
void SetAllowHelp(bool allow);
// Caller must acquire lock before calling.
void SetShutdown();
// Compares the |shutdown_task_count_| to the |thread_count| and returns true
// if they are equal. This check will acquire the |lock_| so the caller
// should not hold the lock when calling this method.
bool ThreadSafeCheckShutdown(int thread_count);
private:
// Both worker threads and controller use the following to synchronize.
Lock lock_;
ConditionVariable work_is_available_; // To tell threads there is work.
// Conditions to notify the controlling process (if it is interested).
ConditionVariable all_threads_have_ids_; // All threads are running.
ConditionVariable no_more_tasks_; // Task count is zero.
const int thread_count_;
scoped_array<PlatformThreadHandle> thread_handles_;
std::vector<int> assignment_history_; // Number of assignment per worker.
std::vector<int> completion_history_; // Number of completions per worker.
int thread_started_counter_; // Used to issue unique id to workers.
int shutdown_task_count_; // Number of tasks told to shutdown
int task_count_; // Number of assignment tasks waiting to be processed.
TimeDelta worker_delay_; // Time each task takes to complete.
bool allow_help_requests_; // Workers can signal more workers.
bool shutdown_; // Set when threads need to terminate.
DFAKE_MUTEX(locked_methods_);
};
//------------------------------------------------------------------------------
// The next section contains the actual tests.
//------------------------------------------------------------------------------
TEST_F(ConditionVariableTest, StartupShutdownTest) {
Lock lock;
// First try trivial startup/shutdown.
{
ConditionVariable cv1(&lock);
} // Call for cv1 destruction.
// Exercise with at least a few waits.
ConditionVariable cv(&lock);
lock.Acquire();
cv.TimedWait(kTenMs); // Wait for 10 ms.
cv.TimedWait(kTenMs); // Wait for 10 ms.
lock.Release();
lock.Acquire();
cv.TimedWait(kTenMs); // Wait for 10 ms.
cv.TimedWait(kTenMs); // Wait for 10 ms.
cv.TimedWait(kTenMs); // Wait for 10 ms.
lock.Release();
} // Call for cv destruction.
TEST_F(ConditionVariableTest, TimeoutTest) {
Lock lock;
ConditionVariable cv(&lock);
lock.Acquire();
TimeTicks start = TimeTicks::Now();
const TimeDelta WAIT_TIME = TimeDelta::FromMilliseconds(300);
// Allow for clocking rate granularity.
const TimeDelta FUDGE_TIME = TimeDelta::FromMilliseconds(50);
cv.TimedWait(WAIT_TIME + FUDGE_TIME);
TimeDelta duration = TimeTicks::Now() - start;
// We can't use EXPECT_GE here as the TimeDelta class does not support the
// required stream conversion.
EXPECT_TRUE(duration >= WAIT_TIME);
lock.Release();
}
// Test serial task servicing, as well as two parallel task servicing methods.
// TODO(maruel): http://crbug.com/10607
TEST_F(ConditionVariableTest, DISABLED_MultiThreadConsumerTest) {
const int kThreadCount = 10;
WorkQueue queue(kThreadCount); // Start the threads.
const int kTaskCount = 10; // Number of tasks in each mini-test here.
base::Time start_time; // Used to time task processing.
{
AutoLock auto_lock(*queue.lock());
while (!queue.EveryIdWasAllocated())
queue.all_threads_have_ids()->Wait();
}
// Wait a bit more to allow threads to reach their wait state.
// If threads aren't in a wait state, they may start to gobble up tasks in
// parallel, short-circuiting (breaking) this test.
PlatformThread::Sleep(100);
{
// Since we have no tasks yet, all threads should be waiting by now.
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetNumberOfCompletedTasks());
// Set up to make one worker do 30ms tasks sequentially.
queue.ResetHistory();
queue.SetTaskCount(kTaskCount);
queue.SetWorkTime(kThirtyMs);
queue.SetAllowHelp(false);
start_time = base::Time::Now();
}
queue.work_is_available()->Signal(); // Start up one thread.
{
// Wait until all 10 work tasks have at least been assigned.
AutoLock auto_lock(*queue.lock());
while(queue.task_count())
queue.no_more_tasks()->Wait();
// The last of the tasks *might* still be running, but... all but one should
// be done by now, since tasks are being done serially.
EXPECT_LE(queue.GetWorkTime().InMilliseconds() * (kTaskCount - 1),
(base::Time::Now() - start_time).InMilliseconds());
EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks());
EXPECT_LE(kTaskCount - 1, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
EXPECT_LE(kTaskCount - 1, queue.GetNumberOfCompletedTasks());
}
// Wait to be sure all tasks are done.
while (1) {
{
AutoLock auto_lock(*queue.lock());
if (kTaskCount == queue.GetNumberOfCompletedTasks())
break;
}
PlatformThread::Sleep(30); // Wait a little.
}
{
// Check that all work was done by one thread id.
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(kTaskCount, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(kTaskCount, queue.GetNumberOfCompletedTasks());
// Set up to make each task include getting help from another worker, so
// so that the work gets done in paralell.
queue.ResetHistory();
queue.SetTaskCount(kTaskCount);
queue.SetWorkTime(kThirtyMs);
queue.SetAllowHelp(true);
start_time = base::Time::Now();
}
queue.work_is_available()->Signal(); // But each worker can signal another.
// Wait to allow the all workers to get done.
while (1) {
{
AutoLock auto_lock(*queue.lock());
if (kTaskCount == queue.GetNumberOfCompletedTasks())
break;
}
PlatformThread::Sleep(30); // Wait a little.
}
{
// Wait until all work tasks have at least been assigned.
AutoLock auto_lock(*queue.lock());
while(queue.task_count())
queue.no_more_tasks()->Wait();
// Since they can all run almost in parallel, there is no guarantee that all
// tasks are finished, but we should have gotten here faster than it would
// take to run all tasks serially.
EXPECT_GT(queue.GetWorkTime().InMilliseconds() * (kTaskCount - 1),
(base::Time::Now() - start_time).InMilliseconds());
// To avoid racy assumptions, we'll just assert that at least 2 threads
// did work.
EXPECT_LE(2, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(kTaskCount, queue.GetNumberOfCompletedTasks());
// Try to ask all workers to help, and only a few will do the work.
queue.ResetHistory();
queue.SetTaskCount(3);
queue.SetWorkTime(kThirtyMs);
queue.SetAllowHelp(false);
}
queue.work_is_available()->Broadcast(); // Make them all try.
// Wait to allow the 3 workers to get done.
PlatformThread::Sleep(45);
{
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
// Set up to make each task get help from another worker.
queue.ResetHistory();
queue.SetTaskCount(3);
queue.SetWorkTime(kThirtyMs);
queue.SetAllowHelp(true); // Allow (unnecessary) help requests.
}
queue.work_is_available()->Broadcast(); // We already signal all threads.
// Wait to allow the 3 workers to get done.
PlatformThread::Sleep(100);
{
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
// Set up to make each task get help from another worker.
queue.ResetHistory();
queue.SetTaskCount(20);
queue.SetWorkTime(kThirtyMs);
queue.SetAllowHelp(true);
}
queue.work_is_available()->Signal(); // But each worker can signal another.
// Wait to allow the 10 workers to get done.
PlatformThread::Sleep(100); // Should take about 60 ms.
{
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
// Same as last test, but with Broadcast().
queue.ResetHistory();
queue.SetTaskCount(20); // 2 tasks per process.
queue.SetWorkTime(kThirtyMs);
queue.SetAllowHelp(true);
}
queue.work_is_available()->Broadcast();
// Wait to allow the 10 workers to get done.
PlatformThread::Sleep(100); // Should take about 60 ms.
{
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
queue.SetShutdown();
}
queue.work_is_available()->Broadcast(); // Force check for shutdown.
SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
queue.ThreadSafeCheckShutdown(kThreadCount));
PlatformThread::Sleep(10); // Be sure they're all shutdown.
}
TEST_F(ConditionVariableTest, LargeFastTaskTest) {
const int kThreadCount = 200;
WorkQueue queue(kThreadCount); // Start the threads.
Lock private_lock; // Used locally for master to wait.
AutoLock private_held_lock(private_lock);
ConditionVariable private_cv(&private_lock);
{
AutoLock auto_lock(*queue.lock());
while (!queue.EveryIdWasAllocated())
queue.all_threads_have_ids()->Wait();
}
// Wait a bit more to allow threads to reach their wait state.
private_cv.TimedWait(kThirtyMs);
{
// Since we have no tasks, all threads should be waiting by now.
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
EXPECT_EQ(0, queue.GetNumberOfCompletedTasks());
// Set up to make all workers do (an average of) 20 tasks.
queue.ResetHistory();
queue.SetTaskCount(20 * kThreadCount);
queue.SetWorkTime(kFortyFiveMs);
queue.SetAllowHelp(false);
}
queue.work_is_available()->Broadcast(); // Start up all threads.
// Wait until we've handed out all tasks.
{
AutoLock auto_lock(*queue.lock());
while (queue.task_count() != 0)
queue.no_more_tasks()->Wait();
}
// Wait till the last of the tasks complete.
// Don't bother to use locks: We may not get info in time... but we'll see it
// eventually.
SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
20 * kThreadCount ==
queue.GetNumberOfCompletedTasks());
{
// With Broadcast(), every thread should have participated.
// but with racing.. they may not all have done equal numbers of tasks.
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_LE(20, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(20 * kThreadCount, queue.GetNumberOfCompletedTasks());
// Set up to make all workers do (an average of) 4 tasks.
queue.ResetHistory();
queue.SetTaskCount(kThreadCount * 4);
queue.SetWorkTime(kFortyFiveMs);
queue.SetAllowHelp(true); // Might outperform Broadcast().
}
queue.work_is_available()->Signal(); // Start up one thread.
// Wait until we've handed out all tasks
{
AutoLock auto_lock(*queue.lock());
while (queue.task_count() != 0)
queue.no_more_tasks()->Wait();
}
// Wait till the last of the tasks complete.
// Don't bother to use locks: We may not get info in time... but we'll see it
// eventually.
SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
4 * kThreadCount ==
queue.GetNumberOfCompletedTasks());
{
// With Signal(), every thread should have participated.
// but with racing.. they may not all have done four tasks.
AutoLock auto_lock(*queue.lock());
EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments());
EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks());
EXPECT_EQ(0, queue.task_count());
EXPECT_LE(4, queue.GetMaxCompletionsByWorkerThread());
EXPECT_EQ(4 * kThreadCount, queue.GetNumberOfCompletedTasks());
queue.SetShutdown();
}
queue.work_is_available()->Broadcast(); // Force check for shutdown.
// Wait for shutdowns to complete.
SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
queue.ThreadSafeCheckShutdown(kThreadCount));
PlatformThread::Sleep(10); // Be sure they're all shutdown.
}
//------------------------------------------------------------------------------
// Finally we provide the implementation for the methods in the WorkQueue class.
//------------------------------------------------------------------------------
WorkQueue::WorkQueue(int thread_count)
: lock_(),
work_is_available_(&lock_),
all_threads_have_ids_(&lock_),
no_more_tasks_(&lock_),
thread_count_(thread_count),
thread_handles_(new PlatformThreadHandle[thread_count]),
assignment_history_(thread_count),
completion_history_(thread_count),
thread_started_counter_(0),
shutdown_task_count_(0),
task_count_(0),
allow_help_requests_(false),
shutdown_(false) {
EXPECT_GE(thread_count_, 1);
ResetHistory();
SetTaskCount(0);
SetWorkTime(TimeDelta::FromMilliseconds(30));
for (int i = 0; i < thread_count_; ++i) {
PlatformThreadHandle pth;
EXPECT_TRUE(PlatformThread::Create(0, this, &pth));
thread_handles_[i] = pth;
}
}
WorkQueue::~WorkQueue() {
{
AutoLock auto_lock(lock_);
SetShutdown();
}
work_is_available_.Broadcast(); // Tell them all to terminate.
for (int i = 0; i < thread_count_; ++i) {
PlatformThread::Join(thread_handles_[i]);
}
}
int WorkQueue::GetThreadId() {
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
DCHECK(!EveryIdWasAllocated());
return thread_started_counter_++; // Give out Unique IDs.
}
bool WorkQueue::EveryIdWasAllocated() const {
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
return thread_count_ == thread_started_counter_;
}
TimeDelta WorkQueue::GetAnAssignment(int thread_id) {
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
DCHECK_LT(0, task_count_);
assignment_history_[thread_id]++;
if (0 == --task_count_) {
no_more_tasks_.Signal();
}
return worker_delay_;
}
void WorkQueue::WorkIsCompleted(int thread_id) {
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
completion_history_[thread_id]++;
}
int WorkQueue::task_count() const {
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
return task_count_;
}
bool WorkQueue::allow_help_requests() const {
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
return allow_help_requests_;
}
bool WorkQueue::shutdown() const {
lock_.AssertAcquired();
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
return shutdown_;
}
// Because this method is called from the test's main thread we need to actually
// take the lock. Threads will call the thread_shutting_down() method with the
// lock already acquired.
bool WorkQueue::ThreadSafeCheckShutdown(int thread_count) {
bool all_shutdown;
AutoLock auto_lock(lock_);
{
// Declare in scope so DFAKE is guranteed to be destroyed before AutoLock.
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
all_shutdown = (shutdown_task_count_ == thread_count);
}
return all_shutdown;
}
void WorkQueue::thread_shutting_down() {
lock_.AssertAcquired();
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
shutdown_task_count_++;
}
Lock* WorkQueue::lock() {
return &lock_;
}
ConditionVariable* WorkQueue::work_is_available() {
return &work_is_available_;
}
ConditionVariable* WorkQueue::all_threads_have_ids() {
return &all_threads_have_ids_;
}
ConditionVariable* WorkQueue::no_more_tasks() {
return &no_more_tasks_;
}
void WorkQueue::ResetHistory() {
for (int i = 0; i < thread_count_; ++i) {
assignment_history_[i] = 0;
completion_history_[i] = 0;
}
}
int WorkQueue::GetMinCompletionsByWorkerThread() const {
int minumum = completion_history_[0];
for (int i = 0; i < thread_count_; ++i)
minumum = std::min(minumum, completion_history_[i]);
return minumum;
}
int WorkQueue::GetMaxCompletionsByWorkerThread() const {
int maximum = completion_history_[0];
for (int i = 0; i < thread_count_; ++i)
maximum = std::max(maximum, completion_history_[i]);
return maximum;
}
int WorkQueue::GetNumThreadsTakingAssignments() const {
int count = 0;
for (int i = 0; i < thread_count_; ++i)
if (assignment_history_[i])
count++;
return count;
}
int WorkQueue::GetNumThreadsCompletingTasks() const {
int count = 0;
for (int i = 0; i < thread_count_; ++i)
if (completion_history_[i])
count++;
return count;
}
int WorkQueue::GetNumberOfCompletedTasks() const {
int total = 0;
for (int i = 0; i < thread_count_; ++i)
total += completion_history_[i];
return total;
}
TimeDelta WorkQueue::GetWorkTime() const {
return worker_delay_;
}
void WorkQueue::SetWorkTime(TimeDelta delay) {
worker_delay_ = delay;
}
void WorkQueue::SetTaskCount(int count) {
task_count_ = count;
}
void WorkQueue::SetAllowHelp(bool allow) {
allow_help_requests_ = allow;
}
void WorkQueue::SetShutdown() {
lock_.AssertAcquired();
shutdown_ = true;
}
//------------------------------------------------------------------------------
// Define the standard worker task. Several tests will spin out many of these
// threads.
//------------------------------------------------------------------------------
// The multithread tests involve several threads with a task to perform as
// directed by an instance of the class WorkQueue.
// The task is to:
// a) Check to see if there are more tasks (there is a task counter).
// a1) Wait on condition variable if there are no tasks currently.
// b) Call a function to see what should be done.
// c) Do some computation based on the number of milliseconds returned in (b).
// d) go back to (a).
// WorkQueue::ThreadMain() implements the above task for all threads.
// It calls the controlling object to tell the creator about progress, and to
// ask about tasks.
void WorkQueue::ThreadMain() {
int thread_id;
{
AutoLock auto_lock(lock_);
thread_id = GetThreadId();
if (EveryIdWasAllocated())
all_threads_have_ids()->Signal(); // Tell creator we're ready.
}
Lock private_lock; // Used to waste time on "our work".
while (1) { // This is the main consumer loop.
TimeDelta work_time;
bool could_use_help;
{
AutoLock auto_lock(lock_);
while (0 == task_count() && !shutdown()) {
work_is_available()->Wait();
}
if (shutdown()) {
// Ack the notification of a shutdown message back to the controller.
thread_shutting_down();
return; // Terminate.
}
// Get our task duration from the queue.
work_time = GetAnAssignment(thread_id);
could_use_help = (task_count() > 0) && allow_help_requests();
} // Release lock
// Do work (outside of locked region.
if (could_use_help)
work_is_available()->Signal(); // Get help from other threads.
if (work_time > TimeDelta::FromMilliseconds(0)) {
// We could just sleep(), but we'll instead further exercise the
// condition variable class, and do a timed wait.
AutoLock auto_lock(private_lock);
ConditionVariable private_cv(&private_lock);
private_cv.TimedWait(work_time); // Unsynchronized waiting.
}
{
AutoLock auto_lock(lock_);
// Send notification that we completed our "work."
WorkIsCompleted(thread_id);
}
}
}
} // namespace

Просмотреть файл

@ -0,0 +1,446 @@
// Copyright (c) 2006-2008 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/condition_variable.h"
#include <stack>
#include "base/lock.h"
#include "base/logging.h"
#include "base/time.h"
using base::TimeDelta;
ConditionVariable::ConditionVariable(Lock* user_lock)
: user_lock_(*user_lock),
run_state_(RUNNING),
allocation_counter_(0),
recycling_list_size_(0) {
DCHECK(user_lock);
}
ConditionVariable::~ConditionVariable() {
AutoLock auto_lock(internal_lock_);
run_state_ = SHUTDOWN; // Prevent any more waiting.
DCHECK_EQ(recycling_list_size_, allocation_counter_);
if (recycling_list_size_ != allocation_counter_) { // Rare shutdown problem.
// There are threads of execution still in this->TimedWait() and yet the
// caller has instigated the destruction of this instance :-/.
// A common reason for such "overly hasty" destruction is that the caller
// was not willing to wait for all the threads to terminate. Such hasty
// actions are a violation of our usage contract, but we'll give the
// waiting thread(s) one last chance to exit gracefully (prior to our
// destruction).
// Note: waiting_list_ *might* be empty, but recycling is still pending.
AutoUnlock auto_unlock(internal_lock_);
Broadcast(); // Make sure all waiting threads have been signaled.
Sleep(10); // Give threads a chance to grab internal_lock_.
// All contained threads should be blocked on user_lock_ by now :-).
} // Reacquire internal_lock_.
DCHECK_EQ(recycling_list_size_, allocation_counter_);
}
void ConditionVariable::Wait() {
// Default to "wait forever" timing, which means have to get a Signal()
// or Broadcast() to come out of this wait state.
TimedWait(TimeDelta::FromMilliseconds(INFINITE));
}
void ConditionVariable::TimedWait(const TimeDelta& max_time) {
Event* waiting_event;
HANDLE handle;
{
AutoLock auto_lock(internal_lock_);
if (RUNNING != run_state_) return; // Destruction in progress.
waiting_event = GetEventForWaiting();
handle = waiting_event->handle();
DCHECK(handle);
} // Release internal_lock.
{
AutoUnlock unlock(user_lock_); // Release caller's lock
WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds()));
// Minimize spurious signal creation window by recycling asap.
AutoLock auto_lock(internal_lock_);
RecycleEvent(waiting_event);
// Release internal_lock_
} // Reacquire callers lock to depth at entry.
}
// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
// a cv_event internally allocated for them) before Broadcast() was called.
void ConditionVariable::Broadcast() {
std::stack<HANDLE> handles; // See FAQ-question-10.
{
AutoLock auto_lock(internal_lock_);
if (waiting_list_.IsEmpty())
return;
while (!waiting_list_.IsEmpty())
// This is not a leak from waiting_list_. See FAQ-question 12.
handles.push(waiting_list_.PopBack()->handle());
} // Release internal_lock_.
while (!handles.empty()) {
SetEvent(handles.top());
handles.pop();
}
}
// Signal() will select one of the waiting threads, and signal it (signal its
// cv_event). For better performance we signal the thread that went to sleep
// most recently (LIFO). If we want fairness, then we wake the thread that has
// been sleeping the longest (FIFO).
void ConditionVariable::Signal() {
HANDLE handle;
{
AutoLock auto_lock(internal_lock_);
if (waiting_list_.IsEmpty())
return; // No one to signal.
// Only performance option should be used.
// This is not a leak from waiting_list. See FAQ-question 12.
handle = waiting_list_.PopBack()->handle(); // LIFO.
} // Release internal_lock_.
SetEvent(handle);
}
// GetEventForWaiting() provides a unique cv_event for any caller that needs to
// wait. This means that (worst case) we may over time create as many cv_event
// objects as there are threads simultaneously using this instance's Wait()
// functionality.
ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
// We hold internal_lock, courtesy of Wait().
Event* cv_event;
if (0 == recycling_list_size_) {
DCHECK(recycling_list_.IsEmpty());
cv_event = new Event();
cv_event->InitListElement();
allocation_counter_++;
// CHECK_NE is not defined in our codebase, so we have to use CHECK
CHECK(cv_event->handle());
} else {
cv_event = recycling_list_.PopFront();
recycling_list_size_--;
}
waiting_list_.PushBack(cv_event);
return cv_event;
}
// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and
// recycles it for use in future Wait() calls for this or other threads.
// Note that there is a tiny chance that the cv_event is still signaled when we
// obtain it, and that can cause spurious signals (if/when we re-use the
// cv_event), but such is quite rare (see FAQ-question-5).
void ConditionVariable::RecycleEvent(Event* used_event) {
// We hold internal_lock, courtesy of Wait().
// If the cv_event timed out, then it is necessary to remove it from
// waiting_list_. If it was selected by Broadcast() or Signal(), then it is
// already gone.
used_event->Extract(); // Possibly redundant
recycling_list_.PushBack(used_event);
recycling_list_size_++;
}
//------------------------------------------------------------------------------
// The next section provides the implementation for the private Event class.
//------------------------------------------------------------------------------
// Event provides a doubly-linked-list of events for use exclusively by the
// ConditionVariable class.
// This custom container was crafted because no simple combination of STL
// classes appeared to support the functionality required. The specific
// unusual requirement for a linked-list-class is support for the Extract()
// method, which can remove an element from a list, potentially for insertion
// into a second list. Most critically, the Extract() method is idempotent,
// turning the indicated element into an extracted singleton whether it was
// contained in a list or not. This functionality allows one (or more) of
// threads to do the extraction. The iterator that identifies this extractable
// element (in this case, a pointer to the list element) can be used after
// arbitrary manipulation of the (possibly) enclosing list container. In
// general, STL containers do not provide iterators that can be used across
// modifications (insertions/extractions) of the enclosing containers, and
// certainly don't provide iterators that can be used if the identified
// element is *deleted* (removed) from the container.
// It is possible to use multiple redundant containers, such as an STL list,
// and an STL map, to achieve similar container semantics. This container has
// only O(1) methods, while the corresponding (multiple) STL container approach
// would have more complex O(log(N)) methods (yeah... N isn't that large).
// Multiple containers also makes correctness more difficult to assert, as
// data is redundantly stored and maintained, which is generally evil.
ConditionVariable::Event::Event() : handle_(0) {
next_ = prev_ = this; // Self referencing circular.
}
ConditionVariable::Event::~Event() {
if (0 == handle_) {
// This is the list holder
while (!IsEmpty()) {
Event* cv_event = PopFront();
DCHECK(cv_event->ValidateAsItem());
delete cv_event;
}
}
DCHECK(IsSingleton());
if (0 != handle_) {
int ret_val = CloseHandle(handle_);
DCHECK(ret_val);
}
}
// Change a container instance permanently into an element of a list.
void ConditionVariable::Event::InitListElement() {
DCHECK(!handle_);
handle_ = CreateEvent(NULL, false, false, NULL);
CHECK(handle_);
}
// Methods for use on lists.
bool ConditionVariable::Event::IsEmpty() const {
DCHECK(ValidateAsList());
return IsSingleton();
}
void ConditionVariable::Event::PushBack(Event* other) {
DCHECK(ValidateAsList());
DCHECK(other->ValidateAsItem());
DCHECK(other->IsSingleton());
// Prepare other for insertion.
other->prev_ = prev_;
other->next_ = this;
// Cut into list.
prev_->next_ = other;
prev_ = other;
DCHECK(ValidateAsDistinct(other));
}
ConditionVariable::Event* ConditionVariable::Event::PopFront() {
DCHECK(ValidateAsList());
DCHECK(!IsSingleton());
return next_->Extract();
}
ConditionVariable::Event* ConditionVariable::Event::PopBack() {
DCHECK(ValidateAsList());
DCHECK(!IsSingleton());
return prev_->Extract();
}
// Methods for use on list elements.
// Accessor method.
HANDLE ConditionVariable::Event::handle() const {
DCHECK(ValidateAsItem());
return handle_;
}
// Pull an element from a list (if it's in one).
ConditionVariable::Event* ConditionVariable::Event::Extract() {
DCHECK(ValidateAsItem());
if (!IsSingleton()) {
// Stitch neighbors together.
next_->prev_ = prev_;
prev_->next_ = next_;
// Make extractee into a singleton.
prev_ = next_ = this;
}
DCHECK(IsSingleton());
return this;
}
// Method for use on a list element or on a list.
bool ConditionVariable::Event::IsSingleton() const {
DCHECK(ValidateLinks());
return next_ == this;
}
// Provide pre/post conditions to validate correct manipulations.
bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
return ValidateLinks() && other->ValidateLinks() && (this != other);
}
bool ConditionVariable::Event::ValidateAsItem() const {
return (0 != handle_) && ValidateLinks();
}
bool ConditionVariable::Event::ValidateAsList() const {
return (0 == handle_) && ValidateLinks();
}
bool ConditionVariable::Event::ValidateLinks() const {
// Make sure both of our neighbors have links that point back to us.
// We don't do the O(n) check and traverse the whole loop, and instead only
// do a local check to (and returning from) our immediate neighbors.
return (next_->prev_ == this) && (prev_->next_ == this);
}
/*
FAQ On subtle implementation details:
1) What makes this problem subtle? Please take a look at "Strategies
for Implementing POSIX Condition Variables on Win32" by Douglas
C. Schmidt and Irfan Pyarali.
http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes
discussions of numerous flawed strategies for implementing this
functionality. I'm not convinced that even the final proposed
implementation has semantics that are as nice as this implementation
(especially with regard to Broadcast() and the impact on threads that
try to Wait() after a Broadcast() has been called, but before all the
original waiting threads have been signaled).
2) Why can't you use a single wait_event for all threads that call
Wait()? See FAQ-question-1, or consider the following: If a single
event were used, then numerous threads calling Wait() could release
their cs locks, and be preempted just before calling
WaitForSingleObject(). If a call to Broadcast() was then presented on
a second thread, it would be impossible to actually signal all
waiting(?) threads. Some number of SetEvent() calls *could* be made,
but there could be no guarantee that those led to to more than one
signaled thread (SetEvent()'s may be discarded after the first!), and
there could be no guarantee that the SetEvent() calls didn't just
awaken "other" threads that hadn't even started waiting yet (oops).
Without any limit on the number of requisite SetEvent() calls, the
system would be forced to do many such calls, allowing many new waits
to receive spurious signals.
3) How does this implementation cause spurious signal events? The
cause in this implementation involves a race between a signal via
time-out and a signal via Signal() or Broadcast(). The series of
actions leading to this are:
a) Timer fires, and a waiting thread exits the line of code:
WaitForSingleObject(waiting_event, max_time.InMilliseconds());
b) That thread (in (a)) is randomly pre-empted after the above line,
leaving the waiting_event reset (unsignaled) and still in the
waiting_list_.
c) A call to Signal() (or Broadcast()) on a second thread proceeds, and
selects the waiting cv_event (identified in step (b)) as the event to revive
via a call to SetEvent().
d) The Signal() method (step c) calls SetEvent() on waiting_event (step b).
e) The waiting cv_event (step b) is now signaled, but no thread is
waiting on it.
f) When that waiting_event (step b) is reused, it will immediately
be signaled (spuriously).
4) Why do you recycle events, and cause spurious signals? First off,
the spurious events are very rare. They can only (I think) appear
when the race described in FAQ-question-3 takes place. This should be
very rare. Most(?) uses will involve only timer expiration, or only
Signal/Broadcast() actions. When both are used, it will be rare that
the race will appear, and it would require MANY Wait() and signaling
activities. If this implementation did not recycle events, then it
would have to create and destroy events for every call to Wait().
That allocation/deallocation and associated construction/destruction
would be costly (per wait), and would only be a rare benefit (when the
race was "lost" and a spurious signal took place). That would be bad
(IMO) optimization trade-off. Finally, such spurious events are
allowed by the specification of condition variables (such as
implemented in Vista), and hence it is better if any user accommodates
such spurious events (see usage note in condition_variable.h).
5) Why don't you reset events when you are about to recycle them, or
about to reuse them, so that the spurious signals don't take place?
The thread described in FAQ-question-3 step c may be pre-empted for an
arbitrary length of time before proceeding to step d. As a result,
the wait_event may actually be re-used *before* step (e) is reached.
As a result, calling reset would not help significantly.
6) How is it that the callers lock is released atomically with the
entry into a wait state? We commit to the wait activity when we
allocate the wait_event for use in a given call to Wait(). This
allocation takes place before the caller's lock is released (and
actually before our internal_lock_ is released). That allocation is
the defining moment when "the wait state has been entered," as that
thread *can* now be signaled by a call to Broadcast() or Signal().
Hence we actually "commit to wait" before releasing the lock, making
the pair effectively atomic.
8) Why do you need to lock your data structures during waiting, as the
caller is already in possession of a lock? We need to Acquire() and
Release() our internal lock during Signal() and Broadcast(). If we tried
to use a callers lock for this purpose, we might conflict with their
external use of the lock. For example, the caller may use to consistently
hold a lock on one thread while calling Signal() on another, and that would
block Signal().
9) Couldn't a more efficient implementation be provided if you
preclude using more than one external lock in conjunction with a
single ConditionVariable instance? Yes, at least it could be viewed
as a simpler API (since you don't have to reiterate the lock argument
in each Wait() call). One of the constructors now takes a specific
lock as an argument, and a there are corresponding Wait() calls that
don't specify a lock now. It turns that the resulting implmentation
can't be made more efficient, as the internal lock needs to be used by
Signal() and Broadcast(), to access internal data structures. As a
result, I was not able to utilize the user supplied lock (which is
being used by the user elsewhere presumably) to protect the private
member access.
9) Since you have a second lock, how can be be sure that there is no
possible deadlock scenario? Our internal_lock_ is always the last
lock acquired, and the first one released, and hence a deadlock (due
to critical section problems) is impossible as a consequence of our
lock.
10) When doing a Broadcast(), why did you copy all the events into
an STL queue, rather than making a linked-loop, and iterating over it?
The iterating during Broadcast() is done so outside the protection
of the internal lock. As a result, other threads, such as the thread
wherein a related event is waiting, could asynchronously manipulate
the links around a cv_event. As a result, the link structure cannot
be used outside a lock. Broadcast() could iterate over waiting
events by cycling in-and-out of the protection of the internal_lock,
but that appears more expensive than copying the list into an STL
stack.
11) Why did the lock.h file need to be modified so much for this
change? Central to a Condition Variable is the atomic release of a
lock during a Wait(). This places Wait() functionality exactly
mid-way between the two classes, Lock and Condition Variable. Given
that there can be nested Acquire()'s of locks, and Wait() had to
Release() completely a held lock, it was necessary to augment the Lock
class with a recursion counter. Even more subtle is the fact that the
recursion counter (in a Lock) must be protected, as many threads can
access it asynchronously. As a positive fallout of this, there are
now some DCHECKS to be sure no one Release()s a Lock more than they
Acquire()ed it, and there is ifdef'ed functionality that can detect
nested locks (legal under windows, but not under Posix).
12) Why is it that the cv_events removed from list in Broadcast() and Signal()
are not leaked? How are they recovered?? The cv_events that appear to leak are
taken from the waiting_list_. For each element in that list, there is currently
a thread in or around the WaitForSingleObject() call of Wait(), and those
threads have references to these otherwise leaked events. They are passed as
arguments to be recycled just aftre returning from WaitForSingleObject().
13) Why did you use a custom container class (the linked list), when STL has
perfectly good containers, such as an STL list? The STL list, as with any
container, does not guarantee the utility of an iterator across manipulation
(such as insertions and deletions) of the underlying container. The custom
double-linked-list container provided that assurance. I don't believe any
combination of STL containers provided the services that were needed at the same
O(1) efficiency as the custom linked list. The unusual requirement
for the container class is that a reference to an item within a container (an
iterator) needed to be maintained across an arbitrary manipulation of the
container. This requirement exposes itself in the Wait() method, where a
waiting_event must be selected prior to the WaitForSingleObject(), and then it
must be used as part of recycling to remove the related instance from the
waiting_list. A hash table (STL map) could be used, but I was embarrased to
use a complex and relatively low efficiency container when a doubly linked list
provided O(1) performance in all required operations. Since other operations
to provide performance-and/or-fairness required queue (FIFO) and list (LIFO)
containers, I would also have needed to use an STL list/queue as well as an STL
map. In the end I decided it would be "fun" to just do it right, and I
put so many assertions (DCHECKs) into the container class that it is trivial to
code review and validate its correctness.
*/

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright (c) 2006-2008 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/cpu.h"
#include <intrin.h>
#include <string>
namespace base {
CPU::CPU()
: type_(0),
family_(0),
model_(0),
stepping_(0),
ext_model_(0),
ext_family_(0),
cpu_vendor_("unknown") {
Initialize();
}
void CPU::Initialize() {
int cpu_info[4] = {-1};
char cpu_string[0x20];
// __cpuid with an InfoType argument of 0 returns the number of
// valid Ids in CPUInfo[0] and the CPU identification string in
// the other three array elements. The CPU identification string is
// not in linear order. The code below arranges the information
// in a human readable form.
//
// More info can be found here:
// http://msdn.microsoft.com/en-us/library/hskdteyh.aspx
__cpuid(cpu_info, 0);
int num_ids = cpu_info[0];
memset(cpu_string, 0, sizeof(cpu_string));
*(reinterpret_cast<int*>(cpu_string)) = cpu_info[1];
*(reinterpret_cast<int*>(cpu_string+4)) = cpu_info[3];
*(reinterpret_cast<int*>(cpu_string+8)) = cpu_info[2];
// Interpret CPU feature information.
if (num_ids > 0) {
__cpuid(cpu_info, 1);
stepping_ = cpu_info[0] & 0xf;
model_ = (cpu_info[0] >> 4) & 0xf;
family_ = (cpu_info[0] >> 8) & 0xf;
type_ = (cpu_info[0] >> 12) & 0x3;
ext_model_ = (cpu_info[0] >> 16) & 0xf;
ext_family_ = (cpu_info[0] >> 20) & 0xff;
cpu_vendor_ = cpu_string;
}
}
} // namespace base

Просмотреть файл

@ -0,0 +1,42 @@
// Copyright (c) 2006-2008 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_CPU_H_
#define BASE_CPU_H_
#include <string>
namespace base {
// Query information about the processor.
class CPU {
public:
// Constructor
CPU();
// Accessors for CPU information.
const std::string& vendor_name() const { return cpu_vendor_; }
int stepping() const { return stepping_; }
int model() const { return model_; }
int family() const { return family_; }
int type() const { return type_; }
int extended_model() const { return ext_model_; }
int extended_family() const { return ext_family_; }
private:
// Query the processor for CPUID information.
void Initialize();
int type_; // process type
int family_; // family of the processor
int model_; // model of processor
int stepping_; // processor revision number
int ext_model_;
int ext_family_;
std::string cpu_vendor_;
};
} // namespace base
#endif // BASE_CPU_H_

Просмотреть файл

@ -0,0 +1,72 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/crypto/cssm_init.h"
#include <Security/cssm.h>
#include "base/logging.h"
#include "base/singleton.h"
// When writing crypto code for Mac OS X, you may find the following
// documentation useful:
// - Common Security: CDSA and CSSM, Version 2 (with corrigenda)
// http://www.opengroup.org/security/cdsa.htm
// - Apple Cryptographic Service Provider Functional Specification
// - CryptoSample: http://developer.apple.com/SampleCode/CryptoSample/
namespace {
class CSSMInitSingleton {
public:
CSSMInitSingleton() : inited_(false), loaded_(false) {
static CSSM_VERSION version = {2, 0};
// TODO(wtc): what should our caller GUID be?
static const CSSM_GUID test_guid = {
0xFADE, 0, 0, { 1, 2, 3, 4, 5, 6, 7, 0 }
};
CSSM_RETURN crtn;
CSSM_PVC_MODE pvc_policy = CSSM_PVC_NONE;
crtn = CSSM_Init(&version, CSSM_PRIVILEGE_SCOPE_NONE, &test_guid,
CSSM_KEY_HIERARCHY_NONE, &pvc_policy, NULL);
if (crtn) {
NOTREACHED();
return;
}
inited_ = true;
crtn = CSSM_ModuleLoad(&gGuidAppleCSP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL);
if (crtn) {
NOTREACHED();
return;
}
loaded_ = true;
}
~CSSMInitSingleton() {
CSSM_RETURN crtn;
if (loaded_) {
crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL);
DCHECK(crtn == CSSM_OK);
}
if (inited_) {
crtn = CSSM_Terminate();
DCHECK(crtn == CSSM_OK);
}
}
private:
bool inited_; // True if CSSM_Init has been called successfully.
bool loaded_; // True if CSSM_ModuleLoad has been called successfully.
};
} // namespace
namespace base {
void EnsureCSSMInit() {
Singleton<CSSMInitSingleton>::get();
}
} // namespace base

Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CRYPTO_CSSM_INIT_H_
#define BASE_CRYPTO_CSSM_INIT_H_
namespace base {
// Initialize CSSM if it isn't already initialized. This must be called before
// any other CSSM functions. This function is thread-safe, and CSSM will only
// ever be initialized once. CSSM will be properly shut down on program exit.
void EnsureCSSMInit();
} // namespace base
#endif // BASE_CRYPTO_CSSM_INIT_H_

Просмотреть файл

@ -0,0 +1,105 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CRYPTO_SIGNATURE_VERIFIER_H_
#define BASE_CRYPTO_SIGNATURE_VERIFIER_H_
#include "build/build_config.h"
#if defined(OS_LINUX)
#include <cryptoht.h>
#elif defined(OS_MACOSX)
#include <Security/cssm.h>
#elif defined(OS_WIN)
#include <windows.h>
#include <wincrypt.h>
#endif
#include <vector>
#include "base/basictypes.h"
namespace base {
// The SignatureVerifier class verifies a signature using a bare public key
// (as opposed to a certificate).
class SignatureVerifier {
public:
SignatureVerifier();
~SignatureVerifier();
// Streaming interface:
// Initiates a signature verification operation. This should be followed
// by one or more VerifyUpdate calls and a VerifyFinal call.
//
// The signature algorithm is specified as a DER encoded ASN.1
// AlgorithmIdentifier structure:
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY DEFINED BY algorithm OPTIONAL }
//
// The signature is encoded according to the signature algorithm, but it
// must not be further encoded in an ASN.1 BIT STRING.
// Note: An RSA signatures is actually a big integer. It must be in the
// big-endian byte order.
//
// The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo
// structure, which contains not only the public key but also its type
// (algorithm):
// SubjectPublicKeyInfo ::= SEQUENCE {
// algorithm AlgorithmIdentifier,
// subjectPublicKey BIT STRING }
bool VerifyInit(const uint8* signature_algorithm,
int signature_algorithm_len,
const uint8* signature,
int signature_len,
const uint8* public_key_info,
int public_key_info_len);
// Feeds a piece of the data to the signature verifier.
void VerifyUpdate(const uint8* data_part, int data_part_len);
// Concludes a signature verification operation. Returns true if the
// signature is valid. Returns false if the signature is invalid or an
// error occurred.
bool VerifyFinal();
// Note: we can provide a one-shot interface if there is interest:
// bool Verify(const uint8* data,
// int data_len,
// const uint8* signature_algorithm,
// int signature_algorithm_len,
// const uint8* signature,
// int signature_len,
// const uint8* public_key_info,
// int public_key_info_len);
private:
void Reset();
std::vector<uint8> signature_;
#if defined(OS_LINUX)
VFYContext* vfy_context_;
#elif defined(OS_MACOSX)
std::vector<uint8> public_key_info_;
CSSM_CSP_HANDLE csp_handle_;
CSSM_CC_HANDLE sig_handle_;
CSSM_KEY public_key_;
#elif defined(OS_WIN)
HCRYPTPROV provider_;
HCRYPTHASH hash_object_;
HCRYPTKEY public_key_;
#endif
};
} // namespace base
#endif // BASE_CRYPTO_SIGNATURE_VERIFIER_H_

Просмотреть файл

@ -0,0 +1,143 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/crypto/signature_verifier.h"
#include <stdlib.h>
#include "base/crypto/cssm_init.h"
#include "base/logging.h"
namespace {
void* AppMalloc(CSSM_SIZE size, void *alloc_ref) {
return malloc(size);
}
void AppFree(void* mem_ptr, void* alloc_ref) {
free(mem_ptr);
}
void* AppRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) {
return realloc(ptr, size);
}
void* AppCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) {
return calloc(num, size);
}
const CSSM_API_MEMORY_FUNCS mem_funcs = {
AppMalloc,
AppFree,
AppRealloc,
AppCalloc,
NULL
};
} // namespace
namespace base {
SignatureVerifier::SignatureVerifier() : csp_handle_(0), sig_handle_(0) {
EnsureCSSMInit();
static CSSM_VERSION version = {2, 0};
CSSM_RETURN crtn;
crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &mem_funcs, 0,
CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE,
NULL, 0, NULL, &csp_handle_);
DCHECK(crtn == CSSM_OK);
}
SignatureVerifier::~SignatureVerifier() {
Reset();
if (csp_handle_) {
CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_);
DCHECK(crtn == CSSM_OK);
}
}
bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
int signature_algorithm_len,
const uint8* signature,
int signature_len,
const uint8* public_key_info,
int public_key_info_len) {
signature_.assign(signature, signature + signature_len);
public_key_info_.assign(public_key_info,
public_key_info + public_key_info_len);
CSSM_ALGORITHMS key_alg = CSSM_ALGID_RSA; // TODO(wtc): hardcoded.
memset(&public_key_, 0, sizeof(public_key_));
public_key_.KeyData.Data = const_cast<uint8*>(&public_key_info_[0]);
public_key_.KeyData.Length = public_key_info_.size();
public_key_.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION;
public_key_.KeyHeader.BlobType = CSSM_KEYBLOB_RAW;
public_key_.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_X509;
public_key_.KeyHeader.AlgorithmId = key_alg;
public_key_.KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
public_key_.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
public_key_.KeyHeader.KeyUsage = CSSM_KEYUSE_VERIFY;
CSSM_KEY_SIZE key_size;
CSSM_RETURN crtn;
crtn = CSSM_QueryKeySizeInBits(csp_handle_, NULL, &public_key_, &key_size);
if (crtn) {
NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn;
return false;
}
public_key_.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits;
// TODO(wtc): decode signature_algorithm...
CSSM_ALGORITHMS sig_alg = CSSM_ALGID_SHA1WithRSA;
crtn = CSSM_CSP_CreateSignatureContext(csp_handle_, sig_alg, NULL,
&public_key_, &sig_handle_);
if (crtn) {
NOTREACHED();
return false;
}
crtn = CSSM_VerifyDataInit(sig_handle_);
if (crtn) {
NOTREACHED();
return false;
}
return true;
}
void SignatureVerifier::VerifyUpdate(const uint8* data_part,
int data_part_len) {
CSSM_DATA data;
data.Data = const_cast<uint8*>(data_part);
data.Length = data_part_len;
CSSM_RETURN crtn = CSSM_VerifyDataUpdate(sig_handle_, &data, 1);
DCHECK(crtn == CSSM_OK);
}
bool SignatureVerifier::VerifyFinal() {
CSSM_DATA sig;
sig.Data = const_cast<uint8*>(&signature_[0]);
sig.Length = signature_.size();
CSSM_RETURN crtn = CSSM_VerifyDataFinal(sig_handle_, &sig);
Reset();
// crtn is CSSMERR_CSP_VERIFY_FAILED if signature verification fails.
return (crtn == CSSM_OK);
}
void SignatureVerifier::Reset() {
CSSM_RETURN crtn;
if (sig_handle_) {
crtn = CSSM_DeleteContext(sig_handle_);
DCHECK(crtn == CSSM_OK);
sig_handle_ = 0;
}
signature_.clear();
// Can't call CSSM_FreeKey on public_key_ because we constructed
// public_key_ manually.
}
} // namespace base

Просмотреть файл

@ -0,0 +1,114 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/crypto/signature_verifier.h"
#include <cryptohi.h>
#include <keyhi.h>
#include <stdlib.h>
#include "base/logging.h"
#include "base/nss_init.h"
namespace base {
SignatureVerifier::SignatureVerifier() : vfy_context_(NULL) {
EnsureNSSInit();
}
SignatureVerifier::~SignatureVerifier() {
Reset();
}
bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
int signature_algorithm_len,
const uint8* signature,
int signature_len,
const uint8* public_key_info,
int public_key_info_len) {
signature_.assign(signature, signature + signature_len);
CERTSubjectPublicKeyInfo* spki = NULL;
SECItem spki_der;
spki_der.type = siBuffer;
spki_der.data = const_cast<uint8*>(public_key_info);
spki_der.len = public_key_info_len;
spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der);
if (!spki)
return false;
SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki);
SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki.
if (!public_key)
return false;
PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
SECKEY_DestroyPublicKey(public_key);
return false;
}
SECItem sig_alg_der;
sig_alg_der.type = siBuffer;
sig_alg_der.data = const_cast<uint8*>(signature_algorithm);
sig_alg_der.len = signature_algorithm_len;
SECAlgorithmID sig_alg_id;
SECStatus rv;
rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, SECOID_AlgorithmIDTemplate,
&sig_alg_der);
if (rv != SECSuccess) {
SECKEY_DestroyPublicKey(public_key);
PORT_FreeArena(arena, PR_TRUE);
return false;
}
SECItem sig;
sig.type = siBuffer;
sig.data = const_cast<uint8*>(signature);
sig.len = signature_len;
SECOidTag hash_alg_tag;
vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig,
&sig_alg_id, &hash_alg_tag,
NULL);
SECKEY_DestroyPublicKey(public_key); // Done with public_key.
PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id.
if (!vfy_context_) {
// A corrupted RSA signature could be detected without the data, so
// VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE
// (-8182).
return false;
}
rv = VFY_Begin(vfy_context_);
if (rv != SECSuccess) {
NOTREACHED();
return false;
}
return true;
}
void SignatureVerifier::VerifyUpdate(const uint8* data_part,
int data_part_len) {
SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len);
DCHECK(rv == SECSuccess);
}
bool SignatureVerifier::VerifyFinal() {
SECStatus rv = VFY_End(vfy_context_);
Reset();
// If signature verification fails, the error code is
// SEC_ERROR_BAD_SIGNATURE (-8182).
return (rv == SECSuccess);
}
void SignatureVerifier::Reset() {
if (vfy_context_) {
VFY_DestroyContext(vfy_context_, PR_TRUE);
vfy_context_ = NULL;
}
signature_.clear();
}
} // namespace base

Просмотреть файл

@ -0,0 +1,268 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/crypto/signature_verifier.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(SignatureVerifierTest, BasicTest) {
// The input data in this test comes from real certificates.
//
// tbs_certificate ("to-be-signed certificate", the part of a certificate
// that is signed), signature_algorithm, and algorithm come from the
// certificate of bugs.webkit.org.
//
// public_key_info comes from the certificate of the issuer, Go Daddy Secure
// Certification Authority.
//
// The bytes in the array initializers are formatted to expose the DER
// encoding of the ASN.1 structures.
// The data that is signed is the following ASN.1 structure:
// TBSCertificate ::= SEQUENCE {
// ... -- omitted, not important
// }
const uint8 tbs_certificate[1017] = {
0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5)
0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31,
0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63,
0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18,
0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64,
0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79,
0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06,
0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32,
0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38,
0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79,
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12,
0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70,
0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49,
0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b,
0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72,
0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72,
0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28,
0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5,
0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0,
0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c,
0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1,
0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff,
0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39,
0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca,
0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1,
0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb,
0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e,
0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30,
0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02,
0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03,
0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16,
0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57,
0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0,
0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73,
0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e,
0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e,
0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b,
0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd,
0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08,
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74,
0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48,
0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5,
0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55,
0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32,
0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76,
0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04,
0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69,
0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69,
0x74, 0x2e, 0x6f, 0x72, 0x67
};
// The signature algorithm is specified as the following ASN.1 structure:
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY DEFINED BY algorithm OPTIONAL }
//
const uint8 signature_algorithm[15] = {
0x30, 0x0d, // a SEQUENCE of length 13 (0xd)
0x06, 0x09, // an OBJECT IDENTIFIER of length 9
// 1.2.840.113549.1.1.5 - sha1WithRSAEncryption
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
0x05, 0x00, // a NULL of length 0
};
// RSA signature, a big integer in the big-endian byte order.
const uint8 signature[256] = {
0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f,
0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37,
0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82,
0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01,
0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01,
0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98,
0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56,
0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8,
0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff,
0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d,
0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84,
0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8,
0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9,
0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2,
0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94,
0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3,
0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a,
0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e,
0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08,
0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd,
0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8,
0x3c, 0xd8, 0x48, 0x8a
};
// The public key is specified as the following ASN.1 structure:
// SubjectPublicKeyInfo ::= SEQUENCE {
// algorithm AlgorithmIdentifier,
// subjectPublicKey BIT STRING }
const uint8 public_key_info[294] = {
0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122)
// algorithm
0x30, 0x0d, // a SEQUENCE of length 13
0x06, 0x09, // an OBJECT IDENTIFIER of length 9
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
0x05, 0x00, // a NULL of length 0
// subjectPublicKey
0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f)
0x00, // number of unused bits
0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a)
// modulus
0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101)
0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec,
0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61,
0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7,
0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c,
0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87,
0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b,
0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9,
0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95,
0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00,
0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f,
0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69,
0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea,
0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48,
0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66,
0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8,
0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7,
0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a,
0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b,
0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0,
0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5,
0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81,
0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87,
0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97,
0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37,
0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe,
0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f,
// public exponent
0x02, 0x03, // an INTEGER of length 3
0x01, 0x00, 0x01
};
// We use the signature verifier to perform four signature verification
// tests.
base::SignatureVerifier verifier;
bool ok;
// Test 1: feed all of the data to the verifier at once (a single
// VerifyUpdate call).
ok = verifier.VerifyInit(signature_algorithm,
sizeof(signature_algorithm),
signature, sizeof(signature),
public_key_info, sizeof(public_key_info));
EXPECT_TRUE(ok);
verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate));
ok = verifier.VerifyFinal();
EXPECT_TRUE(ok);
// Test 2: feed the data to the verifier in three parts (three VerifyUpdate
// calls).
ok = verifier.VerifyInit(signature_algorithm,
sizeof(signature_algorithm),
signature, sizeof(signature),
public_key_info, sizeof(public_key_info));
EXPECT_TRUE(ok);
verifier.VerifyUpdate(tbs_certificate, 256);
verifier.VerifyUpdate(tbs_certificate + 256, 256);
verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512);
ok = verifier.VerifyFinal();
EXPECT_TRUE(ok);
// Test 3: verify the signature with incorrect data.
uint8 bad_tbs_certificate[sizeof(tbs_certificate)];
memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate));
bad_tbs_certificate[10] += 1; // Corrupt one byte of the data.
ok = verifier.VerifyInit(signature_algorithm,
sizeof(signature_algorithm),
signature, sizeof(signature),
public_key_info, sizeof(public_key_info));
EXPECT_TRUE(ok);
verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate));
ok = verifier.VerifyFinal();
// Purify disables digital signature verification, causing the Windows
// CryptoAPI function CryptVerifySignature to always succeed. So we can't
// check the signature verification results of the negative tests when
// running inside Purify. See http://crbug.com/10031.
#ifndef PURIFY
EXPECT_FALSE(ok);
#endif
// Test 4: verify a bad signature.
uint8 bad_signature[sizeof(signature)];
memcpy(bad_signature, signature, sizeof(signature));
bad_signature[10] += 1; // Corrupt one byte of the signature.
ok = verifier.VerifyInit(signature_algorithm,
sizeof(signature_algorithm),
bad_signature, sizeof(bad_signature),
public_key_info, sizeof(public_key_info));
// A crypto library (e.g., NSS) may detect that the signature is corrupted
// and cause VerifyInit to return false, so it is fine for 'ok' to be false.
if (ok) {
verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate));
ok = verifier.VerifyFinal();
#ifndef PURIFY
EXPECT_FALSE(ok);
#endif
}
}

Просмотреть файл

@ -0,0 +1,148 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/crypto/signature_verifier.h"
#include "base/logging.h"
#pragma comment(lib, "crypt32.lib")
namespace {
// Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the
// WINAPI calling convention.
void* WINAPI MyCryptAlloc(size_t size) {
return malloc(size);
}
void WINAPI MyCryptFree(void* p) {
free(p);
}
} // namespace
namespace base {
SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) {
if (!CryptAcquireContext(&provider_, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
provider_ = 0;
}
SignatureVerifier::~SignatureVerifier() {
Reset();
if (provider_) {
BOOL ok = CryptReleaseContext(provider_, 0);
DCHECK(ok);
}
}
bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
int signature_algorithm_len,
const uint8* signature,
int signature_len,
const uint8* public_key_info,
int public_key_info_len) {
signature_.reserve(signature_len);
// CryptoAPI uses big integers in the little-endian byte order, so we need
// to first swap the order of signature bytes.
for (int i = signature_len - 1; i >= 0; --i)
signature_.push_back(signature[i]);
CRYPT_DECODE_PARA decode_para;
decode_para.cbSize = sizeof(decode_para);
decode_para.pfnAlloc = MyCryptAlloc;
decode_para.pfnFree = MyCryptFree;
CERT_PUBLIC_KEY_INFO* cert_public_key_info = NULL;
DWORD struct_len = 0;
BOOL ok;
ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_PUBLIC_KEY_INFO,
public_key_info,
public_key_info_len,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
&decode_para,
&cert_public_key_info,
&struct_len);
if (!ok)
return false;
ok = CryptImportPublicKeyInfo(provider_,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
cert_public_key_info, &public_key_);
free(cert_public_key_info);
if (!ok)
return false;
CRYPT_ALGORITHM_IDENTIFIER* signature_algorithm_id;
struct_len = 0;
ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_ALGORITHM_IDENTIFIER,
signature_algorithm,
signature_algorithm_len,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
&decode_para,
&signature_algorithm_id,
&struct_len);
DCHECK(ok || GetLastError() == ERROR_FILE_NOT_FOUND);
ALG_ID hash_alg_id;
if (ok) {
hash_alg_id = CALG_MD4; // Initialize to a weak hash algorithm that we
// don't support.
if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_SHA1RSA))
hash_alg_id = CALG_SHA1;
else if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_MD5RSA))
hash_alg_id = CALG_MD5;
free(signature_algorithm_id);
DCHECK(hash_alg_id != CALG_MD4);
if (hash_alg_id == CALG_MD4)
return false; // Unsupported hash algorithm.
} else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
// TODO(wtc): X509_ALGORITHM_IDENTIFIER isn't supported on XP SP2. We
// may be able to encapsulate signature_algorithm in a dummy SignedContent
// and decode it with X509_CERT into a CERT_SIGNED_CONTENT_INFO. For now,
// just hardcode the hash algorithm to be SHA-1.
hash_alg_id = CALG_SHA1;
} else {
return false;
}
ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, &hash_object_);
if (!ok)
return false;
return true;
}
void SignatureVerifier::VerifyUpdate(const uint8* data_part,
int data_part_len) {
BOOL ok = CryptHashData(hash_object_, data_part, data_part_len, 0);
DCHECK(ok) << "CryptHashData failed: " << GetLastError();
}
bool SignatureVerifier::VerifyFinal() {
BOOL ok = CryptVerifySignature(hash_object_, &signature_[0],
signature_.size(), public_key_, NULL, 0);
Reset();
if (!ok)
return false;
return true;
}
void SignatureVerifier::Reset() {
BOOL ok;
if (hash_object_) {
ok = CryptDestroyHash(hash_object_);
DCHECK(ok);
hash_object_ = 0;
}
if (public_key_) {
ok = CryptDestroyKey(public_key_);
DCHECK(ok);
public_key_ = 0;
}
signature_.clear();
}
} // namespace base

Двоичные данные
ipc/chromium/src/base/data/data_pack_unittest/sample.pak Normal file

Двоичный файл не отображается.

Двоичные данные
ipc/chromium/src/base/data/file_util_unittest/binary_file.bin Normal file

Двоичный файл не отображается.

Двоичные данные
ipc/chromium/src/base/data/file_util_unittest/binary_file_diff.bin Normal file

Двоичный файл не отображается.

Двоичные данные
ipc/chromium/src/base/data/file_util_unittest/binary_file_same.bin Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1 @@
This file is different.

Просмотреть файл

@ -0,0 +1 @@
this file is the same.

Просмотреть файл

@ -0,0 +1 @@
This file is the same.

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1 @@
This file is the same.

Просмотреть файл

@ -0,0 +1 @@
This file is the same.

Просмотреть файл

@ -0,0 +1 @@
This file is not same.

Просмотреть файл

@ -0,0 +1 @@
This file is the

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,30 @@
# this test causes Purify to get completely confused, aborting the test and
# popping up 10 or more error dialogs
StatsTableTest.MultipleProcesses
# see bug 1151158
# causes purify to occasionally crash, possibly the same reason as 1110206 below
StatsTableTest.MultipleThreads
# this test takes a really long time to run in Purify
TimeTicks.Rollover
TimeTicks.WinRollover
# see bug 1110206
ConditionVariableTest.LargeFastTaskTest
# see bug 1150075
MessageLoopTest.Crasher*
# see bug 1195707
WMIUtilTest.*
# see issue 7412
ScopedTempDir.*
# see issue 7477
ObserverListThreadSafeTest.CrossThreadObserver
ObserverListThreadSafeTest.CrossThreadNotifications
# see bug 10031
SignatureVerifierTest.BasicTest

Просмотреть файл

@ -0,0 +1,97 @@
std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
Alloc Location
...
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::_Mutex::_Mutex(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::ios_base::_Init(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::~LogMessage(void)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
Alloc Location
...
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::_Mutex::_Mutex(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::ios_base::_Init(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::~LogMessage(void)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
CoTaskMemAlloc [OLE32.DLL]
Alloc Location
...
base/wmi_util.cc WMIUtil::CreateLocalConnection(bool)
base/wmi_util.cc WMIProcessUtil::Launch(class std::basic_string const &,int *)
base/wmi_util_unittest.cc WMIUtilTest_TestLaunchProcess_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^

Просмотреть файл

@ -0,0 +1,18 @@
CoTaskMemAlloc [OLE32.DLL]
Alloc Location
...
base/wmi_util.cc WMIUtil::CreateLocalConnection(bool)
base/wmi_util.cc WMIProcessUtil::Launch(class std::basic_string const &,int *)
base/wmi_util_unittest.cc WMIUtilTest_TestLaunchProcess_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
base::ObjectWatcher::StartWatching(void *,Delegate::ObjectWatcher::base *) [base_unittests.exe]
Alloc Location
...
base/object_watcher.cc base::ObjectWatcher::StartWatching(void *,Delegate::ObjectWatcher::base *)
base/directory_watcher_win.cc DirectoryWatcher::Impl::OnObjectSignaled(void *)
base/object_watcher.cc base::ObjectWatcher::Watch::Run(void)
base/message_loop.cc MessageLoop::RunTask(Task *)
^^^

Просмотреть файл

@ -0,0 +1,93 @@
# -----
# Leaks in ::RaiseException, called when we log a fatal error. See bug 1078612.
std::strstreambuf::overflow(int) [base_unittests.exe]
Alloc Location
...
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::_Mutex::_Mutex(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::ios_base::_Init(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::~LogMessage(void)
base/check_handler_unittest.cc ThisFunctionAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::strstreambuf::overflow(int) [base_unittests.exe]
Alloc Location
...
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::_Mutex::_Mutex(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::ios_base::_Init(void) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
Alloc Location
...
base/logging.cc logging::LogMessage::~LogMessage(void)
base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^
# End of leaks in ::RaiseException
# -----

Просмотреть файл

@ -0,0 +1,8 @@
# Probably a Purify error. See bug 1076843.
WideCharToMultiByte: Invalid size (0x27) for destination buffer.
Error Location
...
base/file_util_unittest.cc FileUtilTest_ResolveShortcutTest_Test::TestBody(void)
testing/gtest/src/gtest.cc testing::Test::Run(void)
^^^

Просмотреть файл

Просмотреть файл

@ -0,0 +1,8 @@
# This test currently times out in valgrind, see http://crbug.com/9194
WatchdogTest.AlarmTest
# These tests occassionally hangs under Valgrind on Mac. valgrind-darwin r9573
# Revisit with better valgrind.
# Valgrind bug: https://bugs.kde.org/show_bug.cgi?id=189661
TimerTest.RepeatingTimer
TimerTest.RepeatingTimer_Cancel

Просмотреть файл

@ -0,0 +1,115 @@
// Copyright (c) 2008 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/data_pack.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_piece.h"
// For details of the file layout, see
// http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings
namespace {
static const uint32_t kFileFormatVersion = 1;
// Length of file header: version and entry count.
static const size_t kHeaderLength = 2 * sizeof(uint32_t);
struct DataPackEntry {
uint32_t resource_id;
uint32_t file_offset;
uint32_t length;
static int CompareById(const void* void_key, const void* void_entry) {
uint32_t key = *reinterpret_cast<const uint32_t*>(void_key);
const DataPackEntry* entry =
reinterpret_cast<const DataPackEntry*>(void_entry);
if (key < entry->resource_id) {
return -1;
} else if (key > entry->resource_id) {
return 1;
} else {
return 0;
}
}
} __attribute((packed));
} // anonymous namespace
namespace base {
// In .cc for MemoryMappedFile dtor.
DataPack::DataPack() : resource_count_(0) {
}
DataPack::~DataPack() {
}
bool DataPack::Load(const FilePath& path) {
mmap_.reset(new file_util::MemoryMappedFile);
if (!mmap_->Initialize(path)) {
mmap_.reset();
return false;
}
// Parse the header of the file.
// First uint32_t: version; second: resource count.
const uint32* ptr = reinterpret_cast<const uint32_t*>(mmap_->data());
uint32 version = ptr[0];
if (version != kFileFormatVersion) {
LOG(ERROR) << "Bad data pack version: got " << version << ", expected "
<< kFileFormatVersion;
mmap_.reset();
return false;
}
resource_count_ = ptr[1];
// Sanity check the file.
// 1) Check we have enough entries.
if (kHeaderLength + resource_count_ * sizeof(DataPackEntry) >
mmap_->length()) {
LOG(ERROR) << "Data pack file corruption: too short for number of "
"entries specified.";
mmap_.reset();
return false;
}
// 2) Verify the entries are within the appropriate bounds.
for (size_t i = 0; i < resource_count_; ++i) {
const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>(
mmap_->data() + kHeaderLength + (i * sizeof(DataPackEntry)));
if (entry->file_offset + entry->length > mmap_->length()) {
LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. "
<< "Was the file corrupted?";
mmap_.reset();
return false;
}
}
return true;
}
bool DataPack::Get(uint32_t resource_id, StringPiece* data) {
// It won't be hard to make this endian-agnostic, but it's not worth
// bothering to do right now.
#if defined(__BYTE_ORDER)
// Linux check
COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN,
datapack_assumes_little_endian);
#elif defined(__BIG_ENDIAN__)
// Mac check
#error DataPack assumes little endian
#endif
DataPackEntry* target = reinterpret_cast<DataPackEntry*>(
bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_,
sizeof(DataPackEntry), DataPackEntry::CompareById));
if (!target) {
LOG(ERROR) << "No resource found with id: " << resource_id;
return false;
}
data->set(mmap_->data() + target->file_offset, target->length);
return true;
}
} // namespace base

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) 2008 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.
// DataPack represents a read-only view onto an on-disk file that contains
// (key, value) pairs of data. It's used to store static resources like
// translation strings and images.
#ifndef BASE_DATA_PACK_H_
#define BASE_DATA_PACK_H_
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
namespace file_util {
class MemoryMappedFile;
}
class FilePath;
class StringPiece;
namespace base {
class DataPack {
public:
DataPack();
~DataPack();
// Load a pack file from |path|, returning false on error.
bool Load(const FilePath& path);
// Get resource by id |resource_id|, filling in |data|.
// The data is owned by the DataPack object and should not be modified.
// Returns false if the resource id isn't found.
bool Get(uint32_t resource_id, StringPiece* data);
private:
// The memory-mapped data.
scoped_ptr<file_util::MemoryMappedFile> mmap_;
// Number of resources in the data.
size_t resource_count_;
DISALLOW_COPY_AND_ASSIGN(DataPack);
};
} // namespace base
#endif // BASE_DATA_PACK_H_

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) 2008 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/data_pack.h"
#include "base/file_path.h"
#include "base/path_service.h"
#include "base/string_piece.h"
#include "testing/gtest/include/gtest/gtest.h"
class DataPackTest : public testing::Test {
public:
DataPackTest() {
PathService::Get(base::DIR_SOURCE_ROOT, &data_path_);
data_path_ = data_path_.Append(
FILE_PATH_LITERAL("base/data/data_pack_unittest/sample.pak"));
}
FilePath data_path_;
};
TEST_F(DataPackTest, Load) {
base::DataPack pack;
ASSERT_TRUE(pack.Load(data_path_));
StringPiece data;
ASSERT_TRUE(pack.Get(4, &data));
EXPECT_EQ("this is id 4", data);
ASSERT_TRUE(pack.Get(6, &data));
EXPECT_EQ("this is id 6", data);
// Try reading zero-length data blobs, just in case.
ASSERT_TRUE(pack.Get(1, &data));
EXPECT_EQ(0U, data.length());
ASSERT_TRUE(pack.Get(10, &data));
EXPECT_EQ(0U, data.length());
// Try looking up an invalid key.
ASSERT_FALSE(pack.Get(140, &data));
}

Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) 2006-2008 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 <windows.h>
// Display the command line. This program is designed to be called from
// another process to display assertions. Since the other process has
// complete control of our command line, we assume that it did *not*
// add the program name as the first parameter. This allows us to just
// show the command line directly as the message.
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
LPWSTR cmdline = GetCommandLineW();
MessageBox(NULL, cmdline, L"Kr\x00d8m", MB_TOPMOST);
return 0;
}

Просмотреть файл

@ -0,0 +1,65 @@
// Copyright (c) 2006-2008 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 <windows.h>
#include "base/debug_on_start.h"
#include "base/base_switches.h"
#include "base/basictypes.h"
#include "base/debug_util.h"
// Minimalist implementation to try to find a command line argument. We can use
// kernel32 exported functions but not the CRT functions because we're too early
// in the process startup.
// The code is not that bright and will find things like ---argument or
// /-/argument.
// Note: command_line is non-destructively modified.
bool DebugOnStart::FindArgument(wchar_t* command_line, const wchar_t* argument)
{
int argument_len = lstrlen(argument);
int command_line_len = lstrlen(command_line);
while (command_line_len > argument_len) {
wchar_t first_char = command_line[0];
wchar_t last_char = command_line[argument_len+1];
// Try to find an argument.
if ((first_char == L'-' || first_char == L'/') &&
(last_char == L' ' || last_char == 0 || last_char == L'=')) {
command_line[argument_len+1] = 0;
// Skip the - or /
if (lstrcmpi(command_line+1, argument) == 0) {
// Found it.
command_line[argument_len+1] = last_char;
return true;
}
// Fix back.
command_line[argument_len+1] = last_char;
}
// Continue searching.
++command_line;
--command_line_len;
}
return false;
}
// static
int __cdecl DebugOnStart::Init() {
// Try to find the argument.
if (FindArgument(GetCommandLine(), switches::kDebugOnStart)) {
// We can do 2 things here:
// - Ask for a debugger to attach to us. This involve reading the registry
// key and creating the process.
// - Do a int3.
// It will fails if we run in a sandbox. That is expected.
DebugUtil::SpawnDebuggerOnProcess(GetCurrentProcessId());
// Wait for a debugger to come take us.
DebugUtil::WaitForDebugger(60, false);
} else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) {
// Wait for a debugger to come take us.
DebugUtil::WaitForDebugger(60, true);
}
return 0;
}

Просмотреть файл

@ -0,0 +1,67 @@
// Copyright (c) 2006-2008 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.
// Define the necessary code and global data to look for kDebugOnStart command
// line argument. When the command line argument is detected, it invokes the
// debugger, if no system-wide debugger is registered, a debug break is done.
#ifndef BASE_DEBUG_ON_START_H_
#define BASE_DEBUG_ON_START_H_
#include "base/basictypes.h"
// This only works on Windows.
#if defined(OS_WIN)
#ifndef DECLSPEC_SELECTANY
#define DECLSPEC_SELECTANY __declspec(selectany)
#endif
// Debug on start functions and data.
class DebugOnStart {
public:
// Expected function type in the .CRT$XI* section.
// Note: See VC\crt\src\internal.h for reference.
typedef int (__cdecl *PIFV)(void);
// Looks at the command line for kDebugOnStart argument. If found, it invokes
// the debugger, if this fails, it crashes.
static int __cdecl Init();
// Returns true if the 'argument' is present in the 'command_line'. It does
// not use the CRT, only Kernel32 functions.
static bool FindArgument(wchar_t* command_line, const wchar_t* argument);
};
// Set the function pointer to our function to look for a crash on start. The
// XIB section is started pretty early in the program initialization so in
// theory it should be called before any user created global variable
// initialization code and CRT initialization code.
// Note: See VC\crt\src\defsects.inc and VC\crt\src\crt0.c for reference.
#ifdef _WIN64
// "Fix" the segment. On x64, the .CRT segment is merged into the .rdata segment
// so it contains const data only.
#pragma const_seg(push, ".CRT$XIB")
// Declare the pointer so the CRT will find it.
extern const DebugOnStart::PIFV debug_on_start;
DECLSPEC_SELECTANY const DebugOnStart::PIFV debug_on_start =
&DebugOnStart::Init;
// Fix back the segment.
#pragma const_seg(pop)
#else // _WIN64
// "Fix" the segment. On x86, the .CRT segment is merged into the .data segment
// so it contains non-const data only.
#pragma data_seg(push, ".CRT$XIB")
// Declare the pointer so the CRT will find it.
DECLSPEC_SELECTANY DebugOnStart::PIFV debug_on_start = &DebugOnStart::Init;
// Fix back the segment.
#pragma data_seg(pop)
#endif // _WIN64
#endif // defined(OS_WIN)
#endif // BASE_DEBUG_ON_START_H_

Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) 2006-2008 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/debug_util.h"
#include "base/platform_thread.h"
bool DebugUtil::WaitForDebugger(int wait_seconds, bool silent) {
for (int i = 0; i < wait_seconds * 10; ++i) {
if (BeingDebugged()) {
if (!silent)
BreakDebugger();
return true;
}
PlatformThread::Sleep(100);
}
return false;
}
const void *const *StackTrace::Addresses(size_t* count) {
*count = trace_.size();
if (trace_.size())
return &trace_[0];
return NULL;
}

Просмотреть файл

@ -0,0 +1,70 @@
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a cross platform interface for helper functions related to debuggers.
// You should use this to test if you're running under a debugger, and if you
// would like to yield (breakpoint) into the debugger.
#ifndef BASE_DEBUG_UTIL_H_
#define BASE_DEBUG_UTIL_H_
#include <iostream>
#include <vector>
#include "base/basictypes.h"
// A stacktrace can be helpful in debugging. For example, you can include a
// stacktrace member in a object (probably around #ifndef NDEBUG) so that you
// can later see where the given object was created from.
class StackTrace {
public:
// Create a stacktrace from the current location
StackTrace();
// Get an array of instruction pointer values.
// count: (output) the number of elements in the returned array
const void *const *Addresses(size_t* count);
// Print a backtrace to stderr
void PrintBacktrace();
// Resolve backtrace to symbols and write to stream.
void OutputToStream(std::ostream* os);
private:
std::vector<void*> trace_;
DISALLOW_EVIL_CONSTRUCTORS(StackTrace);
};
class DebugUtil {
public:
// Starts the registered system-wide JIT debugger to attach it to specified
// process.
static bool SpawnDebuggerOnProcess(unsigned process_id);
// Waits wait_seconds seconds for a debugger to attach to the current process.
// When silent is false, an exception is thrown when a debugger is detected.
static bool WaitForDebugger(int wait_seconds, bool silent);
// Are we running under a debugger?
// On OS X, the underlying mechanism doesn't work when the sandbox is enabled.
// To get around this, this function caches its value.
// WARNING: Because of this, on OS X, a call MUST be made to this function
// BEFORE the sandbox is enabled.
static bool BeingDebugged();
// Break into the debugger, assumes a debugger is present.
static void BreakDebugger();
#if defined(OS_MACOSX)
// On OS X, it can take a really long time for the OS Crash handler to
// process a Chrome crash. This translates into a long wait till the process
// actually dies.
// This method disables OS Crash reporting entireley.
// TODO(playmobil): Remove this when we have Breakpad integration enabled -
// see http://crbug.com/7652
static void DisableOSCrashDumps();
#endif // defined(OS_MACOSX)
};
#endif // BASE_DEBUG_UTIL_H_

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug_util.h"
#include <signal.h>
#include "base/basictypes.h"
static void ExitSignalHandler(int sig) {
exit(128 + sig);
}
// static
void DebugUtil::DisableOSCrashDumps() {
int signals_to_intercept[] ={SIGINT,
SIGHUP,
SIGTERM,
SIGABRT,
SIGILL,
SIGTRAP,
SIGEMT,
SIGFPE,
SIGBUS,
SIGSEGV,
SIGSYS,
SIGPIPE,
SIGXCPU,
SIGXFSZ};
// For all these signals, just wire thing sup so we exit immediately.
for (size_t i = 0; i < arraysize(signals_to_intercept); ++i) {
signal(signals_to_intercept[i], ExitSignalHandler);
}
}

Просмотреть файл

@ -0,0 +1,156 @@
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#include "base/debug_util.h"
#include <errno.h>
#include <execinfo.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/basictypes.h"
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/string_piece.h"
// static
bool DebugUtil::SpawnDebuggerOnProcess(unsigned /* process_id */) {
NOTIMPLEMENTED();
return false;
}
#if defined(OS_MACOSX)
// Based on Apple's recommended method as described in
// http://developer.apple.com/qa/qa2004/qa1361.html
// static
bool DebugUtil::BeingDebugged() {
// If the process is sandboxed then we can't use the sysctl, so cache the
// value.
static bool is_set = false;
static bool being_debugged = false;
if (is_set) {
return being_debugged;
}
// Initialize mib, which tells sysctl what info we want. In this case,
// we're looking for information about a specific process ID.
int mib[] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid()
};
// Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and
// binary interfaces may change.
struct kinfo_proc info;
size_t info_size = sizeof(info);
int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
DCHECK(sysctl_result == 0);
if (sysctl_result != 0) {
is_set = true;
being_debugged = false;
return being_debugged;
}
// This process is being debugged if the P_TRACED flag is set.
is_set = true;
being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
return being_debugged;
}
#elif defined(OS_LINUX)
// We can look in /proc/self/status for TracerPid. We are likely used in crash
// handling, so we are careful not to use the heap or have side effects.
// Another option that is common is to try to ptrace yourself, but then we
// can't detach without forking(), and that's not so great.
// static
bool DebugUtil::BeingDebugged() {
int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
return false;
// We assume our line will be in the first 1024 characters and that we can
// read this much all at once. In practice this will generally be true.
// This simplifies and speeds up things considerably.
char buf[1024];
ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
HANDLE_EINTR(close(status_fd));
if (num_read <= 0)
return false;
StringPiece status(buf, num_read);
StringPiece tracer("TracerPid:\t");
StringPiece::size_type pid_index = status.find(tracer);
if (pid_index == StringPiece::npos)
return false;
// Our pid is 0 without a debugger, assume this for any pid starting with 0.
pid_index += tracer.size();
return pid_index < status.size() && status[pid_index] != '0';
}
#endif // OS_LINUX
// static
void DebugUtil::BreakDebugger() {
#if !defined(ARCH_CPU_ARM_FAMILY)
asm ("int3");
#endif
}
StackTrace::StackTrace() {
const int kMaxCallers = 256;
void* callers[kMaxCallers];
int count = backtrace(callers, kMaxCallers);
// Though the backtrace API man page does not list any possible negative
// return values, we still still exclude them because they would break the
// memcpy code below.
if (count > 0) {
trace_.resize(count);
memcpy(&trace_[0], callers, sizeof(callers[0]) * count);
} else {
trace_.resize(0);
}
}
void StackTrace::PrintBacktrace() {
fflush(stderr);
backtrace_symbols_fd(&trace_[0], trace_.size(), STDERR_FILENO);
}
void StackTrace::OutputToStream(std::ostream* os) {
scoped_ptr_malloc<char*> trace_symbols(
backtrace_symbols(&trace_[0], trace_.size()));
// If we can't retrieve the symbols, print an error and just dump the raw
// addresses.
if (trace_symbols.get() == NULL) {
(*os) << "Unable get symbols for backtrace (" << strerror(errno)
<< "). Dumping raw addresses in trace:\n";
for (size_t i = 0; i < trace_.size(); ++i) {
(*os) << "\t" << trace_[i] << "\n";
}
} else {
(*os) << "Backtrace:\n";
for (size_t i = 0; i < trace_.size(); ++i) {
(*os) << "\t" << trace_symbols.get()[i] << "\n";
}
}
}

Просмотреть файл

@ -0,0 +1,77 @@
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sstream>
#include <string>
#include "base/debug_util.h"
#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(StackTrace, OutputToStream) {
StackTrace trace;
// Dump the trace into a string.
std::ostringstream os;
trace.OutputToStream(&os);
std::string backtrace_message = os.str();
size_t frames_found = 0;
trace.Addresses(&frames_found);
if (frames_found == 0) {
LOG(ERROR) << "No stack frames found. Skipping rest of test.";
return;
}
// Check if the output has symbol initialization warning. If it does, fail.
if (backtrace_message.find("Dumping unresolved backtrace") !=
std::string::npos) {
LOG(ERROR) << "Unable to resolve symbols. Skipping rest of test.";
return;
}
#if 0
//TODO(ajwong): Disabling checking of symbol resolution since it depends
// on whether or not symbols are present, and there are too many
// configurations to reliably ensure that symbols are findable.
#if defined(OS_MACOSX)
// Symbol resolution via the backtrace_symbol funciton does not work well
// in OsX.
// See this thread:
//
// http://lists.apple.com/archives/darwin-dev/2009/Mar/msg00111.html
//
// Just check instead that we find our way back to the "start" symbol
// which should be the first symbol in the trace.
//
// TODO(port): Find a more reliable way to resolve symbols.
// Expect to at least find main.
EXPECT_TRUE(backtrace_message.find("start") != std::string::npos)
<< "Expected to find start in backtrace:\n"
<< backtrace_message;
#else // defined(OS_MACOSX)
// Expect to at least find main.
EXPECT_TRUE(backtrace_message.find("main") != std::string::npos)
<< "Expected to find main in backtrace:\n"
<< backtrace_message;
#if defined(OS_WIN)
// MSVC doesn't allow the use of C99's __func__ within C++, so we fake it with
// MSVC's __FUNCTION__ macro.
#define __func__ __FUNCTION__
#endif
// Expect to find this function as well.
// Note: This will fail if not linked with -rdynamic (aka -export_dynamic)
EXPECT_TRUE(backtrace_message.find(__func__) != std::string::npos)
<< "Expected to find " << __func__ << " in backtrace:\n"
<< backtrace_message;
#endif // define(OS_MACOSX)
#endif
}

Просмотреть файл

@ -0,0 +1,289 @@
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug_util.h"
#include <windows.h>
#include <dbghelp.h>
#include "base/basictypes.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/singleton.h"
namespace {
// Minimalist key reader.
// Note: Does not use the CRT.
bool RegReadString(HKEY root, const wchar_t* subkey,
const wchar_t* value_name, wchar_t* buffer, int* len) {
HKEY key = NULL;
DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key);
if (ERROR_SUCCESS != res || key == NULL)
return false;
DWORD type = 0;
DWORD buffer_size = *len * sizeof(wchar_t);
// We don't support REG_EXPAND_SZ.
res = RegQueryValueEx(key, value_name, NULL, &type,
reinterpret_cast<BYTE*>(buffer), &buffer_size);
if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) {
// Make sure the buffer is NULL terminated.
buffer[*len - 1] = 0;
*len = lstrlen(buffer);
RegCloseKey(key);
return true;
}
RegCloseKey(key);
return false;
}
// Replaces each "%ld" in input per a value. Not efficient but it works.
// Note: Does not use the CRT.
bool StringReplace(const wchar_t* input, int value, wchar_t* output,
int output_len) {
memset(output, 0, output_len*sizeof(wchar_t));
int input_len = lstrlen(input);
for (int i = 0; i < input_len; ++i) {
int current_output_len = lstrlen(output);
if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') {
// Make sure we have enough place left.
if ((current_output_len + 12) >= output_len)
return false;
// Cheap _itow().
wsprintf(output+current_output_len, L"%d", value);
i += 2;
} else {
if (current_output_len >= output_len)
return false;
output[current_output_len] = input[i];
}
}
return true;
}
// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
// of functions. The Sym* family of functions may only be invoked by one
// thread at a time. SymbolContext code may access a symbol server over the
// network while holding the lock for this singleton. In the case of high
// latency, this code will adversly affect performance.
//
// There is also a known issue where this backtrace code can interact
// badly with breakpad if breakpad is invoked in a separate thread while
// we are using the Sym* functions. This is because breakpad does now
// share a lock with this function. See this related bug:
//
// http://code.google.com/p/google-breakpad/issues/detail?id=311
//
// This is a very unlikely edge case, and the current solution is to
// just ignore it.
class SymbolContext {
public:
static SymbolContext* Get() {
// We use a leaky singleton because code may call this during process
// termination.
return
Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
}
// Initializes the symbols for the process if it hasn't been done yet.
// Subsequent calls will not reinitialize the symbol, but instead return
// the error code from the first call.
bool Init() {
AutoLock lock(lock_);
if (!initialized_) {
process_ = GetCurrentProcess();
// Defer symbol load until they're needed, use undecorated names, and
// get line numbers.
SymSetOptions(SYMOPT_DEFERRED_LOADS |
SYMOPT_UNDNAME |
SYMOPT_LOAD_LINES);
if (SymInitialize(process_, NULL, TRUE)) {
init_error_ = ERROR_SUCCESS;
} else {
init_error_ = GetLastError();
}
}
initialized_ = true;
return init_error_ == ERROR_SUCCESS;
}
// Returns the error code of a failed initialization. This should only be
// called if Init() has been called. We do not LOG(FATAL) here because
// this code is called might be triggered by a LOG(FATAL) itself. Instead,
// we log an ERROR, and return ERROR_INVALID_DATA.
DWORD init_error() {
if (!initialized_) {
LOG(ERROR) << "Calling GetInitError() before Init() was called. "
<< "Returning ERROR_INVALID_DATA.";
return ERROR_INVALID_DATA;
}
return init_error_;
}
// Returns the process this was initialized for. This should only be
// called if Init() has been called. We LOG(ERROR) in this situation.
// LOG(FATAL) is not used because this code is might be triggered
// by a LOG(FATAL) itself.
HANDLE process() {
if (!initialized_) {
LOG(ERROR) << "Calling process() before Init() was called. "
<< "Returning NULL.";
return NULL;
}
return process_;
}
// For the given trace, attempts to resolve the symbols, and output a trace
// to the ostream os. The format for each line of the backtrace is:
//
// <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
//
// This function should only be called if Init() has been called. We do not
// LOG(FATAL) here because this code is called might be triggered by a
// LOG(FATAL) itself.
void OutputTraceToStream(const std::vector<void*>& trace, std::ostream* os) {
AutoLock lock(lock_);
for (size_t i = 0; (i < trace.size()) && os->good(); ++i) {
const int kMaxNameLength = 256;
DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
// Code adapted from MSDN example:
// http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
ULONG64 buffer[
(sizeof(SYMBOL_INFO) +
kMaxNameLength * sizeof(wchar_t) +
sizeof(ULONG64) - 1) /
sizeof(ULONG64)];
// Initialize symbol information retrieval structures.
DWORD64 sym_displacement = 0;
PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = kMaxNameLength;
BOOL has_symbol = SymFromAddr(process(), frame,
&sym_displacement, symbol);
// Attempt to retrieve line number information.
DWORD line_displacement = 0;
IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
BOOL has_line = SymGetLineFromAddr64(process(), frame,
&line_displacement, &line);
// Output the backtrace line.
(*os) << "\t";
if (has_symbol) {
(*os) << symbol->Name << " [0x" << trace[i] << "+"
<< sym_displacement << "]";
} else {
// If there is no symbol informtion, add a spacer.
(*os) << "(No symbol) [0x" << trace[i] << "]";
}
if (has_line) {
(*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
}
(*os) << "\n";
}
}
SymbolContext()
: initialized_(false),
process_(NULL),
init_error_(ERROR_SUCCESS) {
}
private:
Lock lock_;
bool initialized_;
HANDLE process_;
DWORD init_error_;
DISALLOW_COPY_AND_ASSIGN(SymbolContext);
};
} // namespace
// Note: Does not use the CRT.
bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) {
wchar_t reg_value[1026];
int len = arraysize(reg_value);
if (RegReadString(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug",
L"Debugger", reg_value, &len)) {
wchar_t command_line[1026];
if (StringReplace(reg_value, process_id, command_line,
arraysize(command_line))) {
// We don't mind if the debugger is present because it will simply fail
// to attach to this process.
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
PROCESS_INFORMATION process_info = {0};
if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL,
&startup_info, &process_info)) {
CloseHandle(process_info.hThread);
WaitForInputIdle(process_info.hProcess, 10000);
CloseHandle(process_info.hProcess);
return true;
}
}
}
return false;
}
// static
bool DebugUtil::BeingDebugged() {
return ::IsDebuggerPresent() != 0;
}
// static
void DebugUtil::BreakDebugger() {
__debugbreak();
}
StackTrace::StackTrace() {
// From http://msdn.microsoft.com/en-us/library/bb204633(VS.85).aspx,
// the sum of FramesToSkip and FramesToCapture must be less than 63,
// so set it to 62.
const int kMaxCallers = 62;
void* callers[kMaxCallers];
// TODO(ajwong): Migrate this to StackWalk64.
int count = CaptureStackBackTrace(0, kMaxCallers, callers, NULL);
if (count > 0) {
trace_.resize(count);
memcpy(&trace_[0], callers, sizeof(callers[0]) * count);
} else {
trace_.resize(0);
}
}
void StackTrace::PrintBacktrace() {
OutputToStream(&std::cerr);
}
void StackTrace::OutputToStream(std::ostream* os) {
SymbolContext* context = SymbolContext::Get();
if (context->Init() != ERROR_SUCCESS) {
DWORD error = context->init_error();
(*os) << "Error initializing symbols (" << error
<< "). Dumping unresolved backtrace:\n";
for (size_t i = 0; (i < trace_.size()) && os->good(); ++i) {
(*os) << "\t" << trace_[i] << "\n";
}
} else {
(*os) << "Backtrace:\n";
context->OutputTraceToStream(trace_, os);
}
}

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) 2006-2008 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 module provides a way to monitor a directory for changes.
#ifndef BASE_DIRECTORY_WATCHER_H_
#define BASE_DIRECTORY_WATCHER_H_
#include "base/basictypes.h"
#include "base/ref_counted.h"
class FilePath;
// This class lets you register interest in changes on a directory.
// The delegate will get called whenever a file is added or changed in the
// directory.
class DirectoryWatcher {
public:
class Delegate {
public:
virtual void OnDirectoryChanged(const FilePath& path) = 0;
};
DirectoryWatcher();
~DirectoryWatcher() {}
// Register interest in any changes in the directory |path|.
// OnDirectoryChanged will be called back for each change within the dir.
// If |recursive| is true, the delegate will be notified for each change
// within the directory tree starting at |path|. Returns false on error.
//
// Note: on Windows you may got more notifications for non-recursive watch
// than you expect, especially on versions earlier than Vista. The behavior
// is consistent on any particular version of Windows, but not across
// different versions.
bool Watch(const FilePath& path, Delegate* delegate, bool recursive) {
return impl_->Watch(path, delegate, recursive);
}
// Used internally to encapsulate different members on different platforms.
class PlatformDelegate : public base::RefCounted<PlatformDelegate> {
public:
virtual ~PlatformDelegate() {}
virtual bool Watch(const FilePath& path, Delegate* delegate,
bool recursive) = 0;
};
private:
scoped_refptr<PlatformDelegate> impl_;
DISALLOW_COPY_AND_ASSIGN(DirectoryWatcher);
};
#endif // BASE_DIRECTORY_WATCHER_H_

Просмотреть файл

@ -0,0 +1,321 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/directory_watcher.h"
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <unistd.h>
#include <algorithm>
#include <set>
#include <utility>
#include <vector>
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/hash_tables.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "base/task.h"
#include "base/thread.h"
namespace {
// Singleton to manage all inotify watches.
class InotifyReader {
public:
typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch.
static const Watch kInvalidWatch = -1;
// Watch |path| for changes. |delegate| will be notified on each change. Does
// not check for duplicates. If you call it n times with same |path|
// and |delegate|, it will receive n notifications for each change
// in |path|. It makes implementation of DirectoryWatcher simple.
// Returns kInvalidWatch on failure.
Watch AddWatch(const FilePath& path, DirectoryWatcher::Delegate* delegate);
// Remove |watch| for |delegate|. If you had n watches for same |delegate|
// and path, after calling this function you will have n - 1.
// Returns true on success.
bool RemoveWatch(Watch watch, DirectoryWatcher::Delegate* delegate);
// Callback for InotifyReaderTask.
void OnInotifyEvent(inotify_event* event);
private:
friend struct DefaultSingletonTraits<InotifyReader>;
typedef std::pair<DirectoryWatcher::Delegate*, MessageLoop*> DelegateInfo;
typedef std::multiset<DelegateInfo> DelegateSet;
InotifyReader();
~InotifyReader();
// We keep track of which delegates want to be notified on which watches.
// Multiset is used because there may be many DirectoryWatchers for same path
// and delegate.
base::hash_map<Watch, DelegateSet> delegates_;
// For each watch we also want to know the path it's watching.
base::hash_map<Watch, FilePath> paths_;
// Lock to protect delegates_ and paths_.
Lock lock_;
// Separate thread on which we run blocking read for inotify events.
base::Thread thread_;
// File descriptor returned by inotify_init.
const int inotify_fd_;
// Use self-pipe trick to unblock select during shutdown.
int shutdown_pipe_[2];
// Flag set to true when startup was successful.
bool valid_;
DISALLOW_COPY_AND_ASSIGN(InotifyReader);
};
class InotifyReaderTask : public Task {
public:
InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd)
: reader_(reader),
inotify_fd_(inotify_fd),
shutdown_fd_(shutdown_fd) {
}
virtual void Run() {
while (true) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(inotify_fd_, &rfds);
FD_SET(shutdown_fd_, &rfds);
// Wait until some inotify events are available.
int select_result =
HANDLE_EINTR(select(std::max(inotify_fd_, shutdown_fd_) + 1,
&rfds, NULL, NULL, NULL));
if (select_result < 0) {
DLOG(WARNING) << "select failed: " << strerror(errno);
return;
}
if (FD_ISSET(shutdown_fd_, &rfds))
return;
// Adjust buffer size to current event queue size.
int buffer_size;
int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD,
&buffer_size));
if (ioctl_result != 0) {
DLOG(WARNING) << "ioctl failed: " << strerror(errno);
return;
}
std::vector<char> buffer(buffer_size);
ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd_, &buffer[0],
buffer_size));
if (bytes_read < 0) {
DLOG(WARNING) << "read from inotify fd failed: " << strerror(errno);
return;
}
ssize_t i = 0;
while (i < bytes_read) {
inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
size_t event_size = sizeof(inotify_event) + event->len;
DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
reader_->OnInotifyEvent(event);
i += event_size;
}
}
}
private:
InotifyReader* reader_;
int inotify_fd_;
int shutdown_fd_;
DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask);
};
class InotifyReaderNotifyTask : public Task {
public:
InotifyReaderNotifyTask(DirectoryWatcher::Delegate* delegate,
const FilePath& path)
: delegate_(delegate),
path_(path) {
}
virtual void Run() {
delegate_->OnDirectoryChanged(path_);
}
private:
DirectoryWatcher::Delegate* delegate_;
FilePath path_;
DISALLOW_COPY_AND_ASSIGN(InotifyReaderNotifyTask);
};
InotifyReader::InotifyReader()
: thread_("inotify_reader"),
inotify_fd_(inotify_init()),
valid_(false) {
shutdown_pipe_[0] = -1;
shutdown_pipe_[1] = -1;
if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
thread_.message_loop()->PostTask(
FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0]));
valid_ = true;
}
}
InotifyReader::~InotifyReader() {
if (valid_) {
// Write to the self-pipe so that the select call in InotifyReaderTask
// returns.
HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
thread_.Stop();
}
if (inotify_fd_ >= 0)
close(inotify_fd_);
if (shutdown_pipe_[0] >= 0)
close(shutdown_pipe_[0]);
if (shutdown_pipe_[1] >= 0)
close(shutdown_pipe_[1]);
}
InotifyReader::Watch InotifyReader::AddWatch(
const FilePath& path, DirectoryWatcher::Delegate* delegate) {
if (!valid_)
return kInvalidWatch;
AutoLock auto_lock(lock_);
Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
IN_CREATE | IN_DELETE |
IN_CLOSE_WRITE | IN_MOVE);
if (watch == kInvalidWatch)
return kInvalidWatch;
if (paths_[watch].empty())
paths_[watch] = path; // We don't yet watch this path.
delegates_[watch].insert(std::make_pair(delegate, MessageLoop::current()));
return watch;
}
bool InotifyReader::RemoveWatch(Watch watch,
DirectoryWatcher::Delegate* delegate) {
if (!valid_)
return false;
AutoLock auto_lock(lock_);
if (paths_[watch].empty())
return false; // We don't recognize this watch.
// Only erase one occurrence of delegate (there may be more).
delegates_[watch].erase(
delegates_[watch].find(std::make_pair(delegate, MessageLoop::current())));
if (delegates_[watch].empty()) {
paths_.erase(watch);
delegates_.erase(watch);
return (inotify_rm_watch(inotify_fd_, watch) == 0);
}
return true;
}
void InotifyReader::OnInotifyEvent(inotify_event* event) {
if (event->mask & IN_IGNORED)
return;
DelegateSet delegates_to_notify;
FilePath changed_path;
{
AutoLock auto_lock(lock_);
changed_path = paths_[event->wd];
delegates_to_notify.insert(delegates_[event->wd].begin(),
delegates_[event->wd].end());
}
DelegateSet::iterator i;
for (i = delegates_to_notify.begin(); i != delegates_to_notify.end(); ++i) {
DirectoryWatcher::Delegate* delegate = i->first;
MessageLoop* loop = i->second;
loop->PostTask(FROM_HERE,
new InotifyReaderNotifyTask(delegate, changed_path));
}
}
class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
public:
DirectoryWatcherImpl() : watch_(InotifyReader::kInvalidWatch) {}
~DirectoryWatcherImpl();
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
bool recursive);
private:
// Delegate to notify upon changes.
DirectoryWatcher::Delegate* delegate_;
// Path we're watching (passed to delegate).
FilePath path_;
// Watch returned by InotifyReader.
InotifyReader::Watch watch_;
DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
};
DirectoryWatcherImpl::~DirectoryWatcherImpl() {
if (watch_ != InotifyReader::kInvalidWatch)
Singleton<InotifyReader>::get()->RemoveWatch(watch_, delegate_);
}
bool DirectoryWatcherImpl::Watch(const FilePath& path,
DirectoryWatcher::Delegate* delegate, bool recursive) {
DCHECK(watch_ == InotifyReader::kInvalidWatch); // Can only watch one path.
if (recursive) {
// TODO(phajdan.jr): Support recursive watches.
// Unfortunately inotify has no "native" support for them, but it can be
// emulated by walking the directory tree and setting up watches for each
// directory. Of course this is ineffective for large directory trees.
// For the algorithm, see the link below:
// http://osdir.com/ml/gnome.dashboard.devel/2004-10/msg00022.html
NOTIMPLEMENTED();
return false;
}
delegate_ = delegate;
path_ = path;
watch_ = Singleton<InotifyReader>::get()->AddWatch(path, delegate_);
return watch_ != InotifyReader::kInvalidWatch;
}
} // namespace
DirectoryWatcher::DirectoryWatcher() {
impl_ = new DirectoryWatcherImpl();
}

Просмотреть файл

@ -0,0 +1,121 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/directory_watcher.h"
#include <CoreServices/CoreServices.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_cftyperef.h"
namespace {
const CFAbsoluteTime kEventLatencySeconds = 0.3;
class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
public:
DirectoryWatcherImpl() {}
~DirectoryWatcherImpl() {
if (!path_.value().empty()) {
FSEventStreamStop(fsevent_stream_);
FSEventStreamInvalidate(fsevent_stream_);
FSEventStreamRelease(fsevent_stream_);
}
}
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
bool recursive);
void OnFSEventsCallback(const FilePath& event_path) {
DCHECK(!path_.value().empty());
if (!recursive_) {
FilePath absolute_event_path = event_path;
if (!file_util::AbsolutePath(&absolute_event_path))
return;
if (absolute_event_path != path_)
return;
}
delegate_->OnDirectoryChanged(path_);
}
private:
// Delegate to notify upon changes.
DirectoryWatcher::Delegate* delegate_;
// Path we're watching (passed to delegate).
FilePath path_;
// Indicates recursive watch.
bool recursive_;
// Backend stream we receive event callbacks from (strong reference).
FSEventStreamRef fsevent_stream_;
DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
};
void FSEventsCallback(ConstFSEventStreamRef stream,
void* event_watcher, size_t num_events,
void* event_paths, const FSEventStreamEventFlags flags[],
const FSEventStreamEventId event_ids[]) {
char** paths = reinterpret_cast<char**>(event_paths);
DirectoryWatcherImpl* watcher =
reinterpret_cast<DirectoryWatcherImpl*> (event_watcher);
for (size_t i = 0; i < num_events; i++) {
watcher->OnFSEventsCallback(FilePath(paths[i]));
}
}
bool DirectoryWatcherImpl::Watch(const FilePath& path,
DirectoryWatcher::Delegate* delegate,
bool recursive) {
DCHECK(path_.value().empty()); // Can only watch one path.
DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
if (!file_util::PathExists(path))
return false;
path_ = path;
if (!file_util::AbsolutePath(&path_)) {
path_ = FilePath(); // Make sure we're marked as not-in-use.
return false;
}
delegate_ = delegate;
recursive_ = recursive;
scoped_cftyperef<CFStringRef> cf_path(CFStringCreateWithCString(
NULL, path.value().c_str(), kCFStringEncodingMacHFS));
CFStringRef path_for_array = cf_path.get();
scoped_cftyperef<CFArrayRef> watched_paths(CFArrayCreate(
NULL, reinterpret_cast<const void**>(&path_for_array), 1,
&kCFTypeArrayCallBacks));
FSEventStreamContext context;
context.version = 0;
context.info = this;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
watched_paths,
kFSEventStreamEventIdSinceNow,
kEventLatencySeconds,
kFSEventStreamCreateFlagNone);
FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
FSEventStreamStart(fsevent_stream_);
return true;
}
} // namespace
DirectoryWatcher::DirectoryWatcher() {
impl_ = new DirectoryWatcherImpl();
}

Просмотреть файл

@ -0,0 +1,20 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file exists for Linux systems which don't have the inotify headers, and
// thus cannot build directory_watcher_inotify.cc
#include "base/directory_watcher.h"
class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
public:
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
bool recursive) {
return false;
}
};
DirectoryWatcher::DirectoryWatcher() {
impl_ = new DirectoryWatcherImpl();
}

Просмотреть файл

@ -0,0 +1,406 @@
// Copyright (c) 2008 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/directory_watcher.h"
#include <limits>
#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/platform_thread.h"
#include "base/string_util.h"
#if defined(OS_WIN)
#include "base/win_util.h"
#endif // defined(OS_WIN)
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// For tests where we wait a bit to verify nothing happened
const int kWaitForEventTime = 1000;
class DirectoryWatcherTest : public testing::Test {
public:
// Implementation of DirectoryWatcher on Mac requires UI loop.
DirectoryWatcherTest() : loop_(MessageLoop::TYPE_UI) {
}
void OnTestDelegateFirstNotification(const FilePath& path) {
notified_delegates_++;
if (notified_delegates_ >= expected_notified_delegates_)
MessageLoop::current()->Quit();
}
protected:
virtual void SetUp() {
// Name a subdirectory of the temp directory.
FilePath path;
ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path));
test_dir_ = path.Append(FILE_PATH_LITERAL("DirectoryWatcherTest"));
// Create a fresh, empty copy of this directory.
file_util::Delete(test_dir_, true);
file_util::CreateDirectory(test_dir_);
}
virtual void TearDown() {
// Make sure there are no tasks in the loop.
loop_.RunAllPending();
// Clean up test directory.
ASSERT_TRUE(file_util::Delete(test_dir_, true));
ASSERT_FALSE(file_util::PathExists(test_dir_));
}
// Write |content| to the |filename|. Returns true on success.
bool WriteTestFile(const FilePath& filename,
const std::string& content) {
return (file_util::WriteFile(filename, content.c_str(), content.length()) ==
static_cast<int>(content.length()));
}
// Create directory |name| under test_dir_. If |sync| is true, runs
// SyncIfPOSIX. Returns path to the created directory, including test_dir_.
FilePath CreateTestDirDirectoryASCII(const std::string& name, bool sync) {
FilePath path(test_dir_.AppendASCII(name));
EXPECT_TRUE(file_util::CreateDirectory(path));
if (sync)
SyncIfPOSIX();
return path;
}
void SetExpectedNumberOfNotifiedDelegates(int n) {
notified_delegates_ = 0;
expected_notified_delegates_ = n;
}
void VerifyExpectedNumberOfNotifiedDelegates() {
// Check that we get at least the expected number of notified delegates.
if (expected_notified_delegates_ - notified_delegates_ > 0)
loop_.Run();
// Check that we get no more than the expected number of notified delegates.
loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
kWaitForEventTime);
loop_.Run();
EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
}
// We need this function for reliable tests on Mac OS X. FSEvents API
// has a latency interval and can merge multiple events into one,
// and we need a clear distinction between events triggered by test setup code
// and test code.
void SyncIfPOSIX() {
#if defined(OS_POSIX)
sync();
#endif // defined(OS_POSIX)
}
MessageLoop loop_;
// The path to a temporary directory used for testing.
FilePath test_dir_;
// The number of test delegates which received their notification.
int notified_delegates_;
// The number of notified test delegates after which we quit the message loop.
int expected_notified_delegates_;
};
class TestDelegate : public DirectoryWatcher::Delegate {
public:
TestDelegate(DirectoryWatcherTest* test)
: test_(test),
got_notification_(false),
original_thread_id_(PlatformThread::CurrentId()) {
}
bool got_notification() const {
return got_notification_;
}
void reset() {
got_notification_ = false;
}
virtual void OnDirectoryChanged(const FilePath& path) {
EXPECT_EQ(original_thread_id_, PlatformThread::CurrentId());
if (!got_notification_)
test_->OnTestDelegateFirstNotification(path);
got_notification_ = true;
}
private:
// Hold a pointer to current test fixture to inform it on first notification.
DirectoryWatcherTest* test_;
// Set to true after first notification.
bool got_notification_;
// Keep track of original thread id to verify that callbacks are called
// on the same thread.
PlatformThreadId original_thread_id_;
};
// Basic test: add a file and verify we notice it.
TEST_F(DirectoryWatcherTest, NewFile) {
DirectoryWatcher watcher;
TestDelegate delegate(this);
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
// Verify that modifying a file is caught.
TEST_F(DirectoryWatcherTest, ModifiedFile) {
// Write a file to the test dir.
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
SyncIfPOSIX();
DirectoryWatcher watcher;
TestDelegate delegate(this);
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
// Now make sure we get notified if the file is modified.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "new content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
TEST_F(DirectoryWatcherTest, DeletedFile) {
// Write a file to the test dir.
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
SyncIfPOSIX();
DirectoryWatcher watcher;
TestDelegate delegate(this);
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
// Now make sure we get notified if the file is deleted.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(file_util::Delete(test_dir_.AppendASCII("test_file"), false));
VerifyExpectedNumberOfNotifiedDelegates();
}
// Verify that letting the watcher go out of scope stops notifications.
TEST_F(DirectoryWatcherTest, Unregister) {
TestDelegate delegate(this);
{
DirectoryWatcher watcher;
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
// And then let it fall out of scope, clearing its watch.
}
// Write a file to the test dir.
SetExpectedNumberOfNotifiedDelegates(0);
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
TEST_F(DirectoryWatcherTest, SubDirRecursive) {
FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true));
#if defined(OS_LINUX)
// TODO(port): Recursive watches are not implemented on Linux.
return;
#endif // !defined(OS_WIN)
// Verify that modifications to a subdirectory are noticed by recursive watch.
TestDelegate delegate(this);
DirectoryWatcher watcher;
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
// Write a file to the subdir.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
TEST_F(DirectoryWatcherTest, SubDirNonRecursive) {
#if defined(OS_WIN)
// Disable this test for earlier version of Windows. It turned out to be
// very difficult to create a reliable test for them.
if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)
return;
#endif // defined(OS_WIN)
FilePath subdir(CreateTestDirDirectoryASCII("SubDir", false));
// Create a test file before the test. On Windows we get a notification
// when creating a file in a subdir even with a non-recursive watch.
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content"));
SyncIfPOSIX();
// Verify that modifications to a subdirectory are not noticed
// by a not-recursive watch.
DirectoryWatcher watcher;
TestDelegate delegate(this);
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
// Modify the test file. There should be no notifications.
SetExpectedNumberOfNotifiedDelegates(0);
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "other content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
namespace {
// Used by the DeleteDuringNotify test below.
// Deletes the DirectoryWatcher when it's notified.
class Deleter : public DirectoryWatcher::Delegate {
public:
Deleter(DirectoryWatcher* watcher, MessageLoop* loop)
: watcher_(watcher),
loop_(loop) {
}
virtual void OnDirectoryChanged(const FilePath& path) {
watcher_.reset(NULL);
loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}
scoped_ptr<DirectoryWatcher> watcher_;
MessageLoop* loop_;
};
} // anonymous namespace
// Verify that deleting a watcher during the callback
TEST_F(DirectoryWatcherTest, DeleteDuringNotify) {
DirectoryWatcher* watcher = new DirectoryWatcher;
Deleter deleter(watcher, &loop_); // Takes ownership of watcher.
ASSERT_TRUE(watcher->Watch(test_dir_, &deleter, false));
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
loop_.Run();
// We win if we haven't crashed yet.
// Might as well double-check it got deleted, too.
ASSERT_TRUE(deleter.watcher_.get() == NULL);
}
TEST_F(DirectoryWatcherTest, MultipleWatchersSingleFile) {
DirectoryWatcher watcher1, watcher2;
TestDelegate delegate1(this), delegate2(this);
ASSERT_TRUE(watcher1.Watch(test_dir_, &delegate1, false));
ASSERT_TRUE(watcher2.Watch(test_dir_, &delegate2, false));
SetExpectedNumberOfNotifiedDelegates(2);
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
TEST_F(DirectoryWatcherTest, MultipleWatchersDifferentFiles) {
const int kNumberOfWatchers = 5;
DirectoryWatcher watchers[kNumberOfWatchers];
TestDelegate delegates[kNumberOfWatchers] = {this, this, this, this, this};
FilePath subdirs[kNumberOfWatchers];
for (int i = 0; i < kNumberOfWatchers; i++) {
subdirs[i] = CreateTestDirDirectoryASCII("Dir" + IntToString(i), false);
ASSERT_TRUE(watchers[i].Watch(subdirs[i], &delegates[i], false));
}
for (int i = 0; i < kNumberOfWatchers; i++) {
// Verify that we only get modifications from one watcher (each watcher has
// different directory).
for (int j = 0; j < kNumberOfWatchers; j++)
delegates[j].reset();
// Write a file to the subdir.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdirs[i].AppendASCII("test_file"), "content"));
VerifyExpectedNumberOfNotifiedDelegates();
loop_.RunAllPending();
}
}
#if defined(OS_WIN) || defined(OS_MACOSX)
// TODO(phajdan.jr): Enable when support for Linux recursive watches is added.
TEST_F(DirectoryWatcherTest, WatchCreatedDirectory) {
TestDelegate delegate(this);
DirectoryWatcher watcher;
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
SetExpectedNumberOfNotifiedDelegates(1);
FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true));
// Create a file inside the subdir to force Windows to fire notifications.
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content"));
VerifyExpectedNumberOfNotifiedDelegates();
delegate.reset();
// Verify that changes inside the subdir are noticed.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "other content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
TEST_F(DirectoryWatcherTest, RecursiveWatchDeletedSubdirectory) {
FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true));
TestDelegate delegate(this);
DirectoryWatcher watcher;
ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
// Write a file to the subdir.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content"));
VerifyExpectedNumberOfNotifiedDelegates();
delegate.reset();
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(file_util::Delete(subdir, true));
VerifyExpectedNumberOfNotifiedDelegates();
}
TEST_F(DirectoryWatcherTest, MoveFileAcrossWatches) {
FilePath subdir1(CreateTestDirDirectoryASCII("SubDir1", true));
FilePath subdir2(CreateTestDirDirectoryASCII("SubDir2", true));
TestDelegate delegate1(this), delegate2(this);
DirectoryWatcher watcher1, watcher2;
ASSERT_TRUE(watcher1.Watch(subdir1, &delegate1, true));
ASSERT_TRUE(watcher2.Watch(subdir2, &delegate2, true));
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir1.AppendASCII("file"), "some content"));
SyncIfPOSIX();
VerifyExpectedNumberOfNotifiedDelegates();
delegate1.reset();
delegate2.reset();
SetExpectedNumberOfNotifiedDelegates(2);
ASSERT_TRUE(file_util::Move(subdir1.AppendASCII("file"),
subdir2.AppendASCII("file")));
VerifyExpectedNumberOfNotifiedDelegates();
delegate1.reset();
delegate2.reset();
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir2.AppendASCII("file"), "other content"));
VerifyExpectedNumberOfNotifiedDelegates();
}
#endif // defined(OS_WIN) || defined(OS_MACOSX)
// Verify that watching a directory that doesn't exist fails, but doesn't
// asssert.
// Basic test: add a file and verify we notice it.
TEST_F(DirectoryWatcherTest, NonExistentDirectory) {
DirectoryWatcher watcher;
ASSERT_FALSE(watcher.Watch(test_dir_.AppendASCII("does-not-exist"), NULL,
false));
}
} // namespace

Просмотреть файл

@ -0,0 +1,82 @@
// Copyright (c) 2006-2008 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/directory_watcher.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/object_watcher.h"
#include "base/ref_counted.h"
namespace {
class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate,
public base::ObjectWatcher::Delegate {
public:
DirectoryWatcherImpl() : handle_(INVALID_HANDLE_VALUE) {}
virtual ~DirectoryWatcherImpl();
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
bool recursive);
// Callback from MessageLoopForIO.
virtual void OnObjectSignaled(HANDLE object);
private:
// Delegate to notify upon changes.
DirectoryWatcher::Delegate* delegate_;
// Path we're watching (passed to delegate).
FilePath path_;
// Handle for FindFirstChangeNotification.
HANDLE handle_;
// ObjectWatcher to watch handle_ for events.
base::ObjectWatcher watcher_;
DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
};
DirectoryWatcherImpl::~DirectoryWatcherImpl() {
if (handle_ != INVALID_HANDLE_VALUE) {
watcher_.StopWatching();
FindCloseChangeNotification(handle_);
}
}
bool DirectoryWatcherImpl::Watch(const FilePath& path,
DirectoryWatcher::Delegate* delegate, bool recursive) {
DCHECK(path_.value().empty()); // Can only watch one path.
handle_ = FindFirstChangeNotification(
path.value().c_str(),
recursive,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE);
if (handle_ == INVALID_HANDLE_VALUE)
return false;
delegate_ = delegate;
path_ = path;
watcher_.StartWatching(handle_, this);
return true;
}
void DirectoryWatcherImpl::OnObjectSignaled(HANDLE object) {
DCHECK(object == handle_);
// Make sure we stay alive through the body of this function.
scoped_refptr<DirectoryWatcherImpl> keep_alive(this);
delegate_->OnDirectoryChanged(path_);
// Register for more notifications on file change.
BOOL ok = FindNextChangeNotification(object);
DCHECK(ok);
watcher_.StartWatching(object, this);
}
} // namespace
DirectoryWatcher::DirectoryWatcher() {
impl_ = new DirectoryWatcherImpl();
}

Просмотреть файл

@ -0,0 +1,33 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This provides a wrapper around system calls which may be interrupted by a
// signal and return EINTR. See man 7 signal.
//
// On Windows, this wrapper macro does nothing.
#ifndef BASE_EINTR_WRAPPER_H_
#define BASE_EINTR_WRAPPER_H_
#include "build/build_config.h"
#if defined(OS_POSIX)
#include <errno.h>
#define HANDLE_EINTR(x) ({ \
typeof(x) __eintr_result__; \
do { \
__eintr_result__ = x; \
} while (__eintr_result__ == -1 && errno == EINTR); \
__eintr_result__;\
})
#else
#define HANDLE_EINTR(x) x
#endif // OS_POSIX
#endif // !BASE_EINTR_WRAPPER_H_

Просмотреть файл

@ -0,0 +1,259 @@
// Copyright (c) 2006-2008 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 "build/build_config.h"
#include <windows.h>
#include <mmsystem.h>
#include "base/event_recorder.h"
#include "base/file_util.h"
#include "base/logging.h"
// A note about time.
// For perfect playback of events, you'd like a very accurate timer
// so that events are played back at exactly the same time that
// they were recorded. However, windows has a clock which is only
// granular to ~15ms. We see more consistent event playback when
// using a higher resolution timer. To do this, we use the
// timeGetTime API instead of the default GetTickCount() API.
namespace base {
EventRecorder* EventRecorder::current_ = NULL;
LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
LPARAM lParam) {
CHECK(EventRecorder::current());
return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
}
LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
LPARAM lParam) {
CHECK(EventRecorder::current());
return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
}
EventRecorder::~EventRecorder() {
// Try to assert early if the caller deletes the recorder
// while it is still in use.
DCHECK(!journal_hook_);
DCHECK(!is_recording_ && !is_playing_);
}
bool EventRecorder::StartRecording(const FilePath& filename) {
if (journal_hook_ != NULL)
return false;
if (is_recording_ || is_playing_)
return false;
// Open the recording file.
DCHECK(file_ == NULL);
file_ = file_util::OpenFile(filename, "wb+");
if (!file_) {
DLOG(ERROR) << "EventRecorder could not open log file";
return false;
}
// Set the faster clock, if possible.
::timeBeginPeriod(1);
// Set the recording hook. JOURNALRECORD can only be used as a global hook.
journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
GetModuleHandle(NULL), 0);
if (!journal_hook_) {
DLOG(ERROR) << "EventRecorder Record Hook failed";
file_util::CloseFile(file_);
return false;
}
is_recording_ = true;
return true;
}
void EventRecorder::StopRecording() {
if (is_recording_) {
DCHECK(journal_hook_ != NULL);
if (!::UnhookWindowsHookEx(journal_hook_)) {
DLOG(ERROR) << "EventRecorder Unhook failed";
// Nothing else we can really do here.
return;
}
::timeEndPeriod(1);
DCHECK(file_ != NULL);
file_util::CloseFile(file_);
file_ = NULL;
journal_hook_ = NULL;
is_recording_ = false;
}
}
bool EventRecorder::StartPlayback(const FilePath& filename) {
if (journal_hook_ != NULL)
return false;
if (is_recording_ || is_playing_)
return false;
// Open the recording file.
DCHECK(file_ == NULL);
file_ = file_util::OpenFile(filename, "rb");
if (!file_) {
DLOG(ERROR) << "EventRecorder Playback could not open log file";
return false;
}
// Read the first event from the record.
if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
DLOG(ERROR) << "EventRecorder Playback has no records!";
file_util::CloseFile(file_);
return false;
}
// Set the faster clock, if possible.
::timeBeginPeriod(1);
// Playback time is tricky. When playing back, we read a series of events,
// each with timeouts. Simply subtracting the delta between two timers will
// lead to fast playback (about 2x speed). The API has two events, one
// which advances to the next event (HC_SKIP), and another that requests the
// event (HC_GETNEXT). The same event will be requested multiple times.
// Each time the event is requested, we must calculate the new delay.
// To do this, we track the start time of the playback, and constantly
// re-compute the delay. I mention this only because I saw two examples
// of how to use this code on the net, and both were broken :-)
playback_start_time_ = timeGetTime();
playback_first_msg_time_ = playback_msg_.time;
// Set the hook. JOURNALPLAYBACK can only be used as a global hook.
journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
GetModuleHandle(NULL), 0);
if (!journal_hook_) {
DLOG(ERROR) << "EventRecorder Playback Hook failed";
return false;
}
is_playing_ = true;
return true;
}
void EventRecorder::StopPlayback() {
if (is_playing_) {
DCHECK(journal_hook_ != NULL);
if (!::UnhookWindowsHookEx(journal_hook_)) {
DLOG(ERROR) << "EventRecorder Unhook failed";
// Nothing else we can really do here.
}
DCHECK(file_ != NULL);
file_util::CloseFile(file_);
file_ = NULL;
::timeEndPeriod(1);
journal_hook_ = NULL;
is_playing_ = false;
}
}
// Windows callback hook for the recorder.
LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
static bool recording_enabled = true;
EVENTMSG* msg_ptr = NULL;
// The API says we have to do this.
// See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
if (nCode < 0)
return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
// Check for the break key being pressed and stop recording.
if (::GetKeyState(VK_CANCEL) & 0x8000) {
StopRecording();
return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
}
// The Journal Recorder must stop recording events when system modal
// dialogs are present. (see msdn link above)
switch(nCode) {
case HC_SYSMODALON:
recording_enabled = false;
break;
case HC_SYSMODALOFF:
recording_enabled = true;
break;
}
if (nCode == HC_ACTION && recording_enabled) {
// Aha - we have an event to record.
msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
msg_ptr->time = timeGetTime();
fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
fflush(file_);
}
return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
}
// Windows callback for the playback mode.
LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam,
LPARAM lParam) {
static bool playback_enabled = true;
int delay = 0;
switch(nCode) {
// A system modal dialog box is being displayed. Stop playing back
// messages.
case HC_SYSMODALON:
playback_enabled = false;
break;
// A system modal dialog box is destroyed. We can start playing back
// messages again.
case HC_SYSMODALOFF:
playback_enabled = true;
break;
// Prepare to copy the next mouse or keyboard event to playback.
case HC_SKIP:
if (!playback_enabled)
break;
// Read the next event from the record.
if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
this->StopPlayback();
break;
// Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
case HC_GETNEXT:
if (!playback_enabled)
break;
memcpy(reinterpret_cast<void*>(lParam), &playback_msg_,
sizeof(playback_msg_));
// The return value is the amount of time (in milliseconds) to wait
// before playing back the next message in the playback queue. Each
// time this is called, we recalculate the delay relative to our current
// wall clock.
delay = (playback_msg_.time - playback_first_msg_time_) -
(timeGetTime() - playback_start_time_);
if (delay < 0)
delay = 0;
return delay;
// An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
// indicating that the message is not removed from the message queue after
// PeekMessage processing.
case HC_NOREMOVE:
break;
}
return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
}
} // namespace base

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше